Merge branch 'development' into Prefabs/ProcessStackAddPrefabBug
This commit is contained in:
@@ -484,6 +484,7 @@ namespace AZ::IO
|
||||
// as_posix
|
||||
//! Replicates the behavior of the Python pathlib as_posix method
|
||||
//! by replacing the Windows Path Separator with the Posix Path Seperator
|
||||
constexpr string_type AsPosix() const;
|
||||
AZStd::string StringAsPosix() const;
|
||||
constexpr AZStd::fixed_string<MaxPathLength> FixedMaxPathStringAsPosix() const noexcept;
|
||||
|
||||
|
||||
@@ -1043,6 +1043,13 @@ namespace AZ::IO
|
||||
// as_posix
|
||||
// Returns a copy of the path with the path separators converted to PosixPathSeparator
|
||||
template <typename StringType>
|
||||
constexpr auto BasicPath<StringType>::AsPosix() const -> string_type
|
||||
{
|
||||
string_type resultPath(m_path.begin(), m_path.end());
|
||||
AZStd::replace(resultPath.begin(), resultPath.end(), WindowsPathSeparator, PosixPathSeparator);
|
||||
return resultPath;
|
||||
}
|
||||
template <typename StringType>
|
||||
AZStd::string BasicPath<StringType>::StringAsPosix() const
|
||||
{
|
||||
AZStd::string resultPath(m_path.begin(), m_path.end());
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
*/
|
||||
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/Serialization/Json/RegistrationContext.h>
|
||||
#include <AzCore/Serialization/Json/PathSerializer.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/std/functional.h>
|
||||
|
||||
@@ -35,10 +37,8 @@ namespace AZ::IO
|
||||
size_t Save(const void* classPtr, IO::GenericStream& stream, bool) override
|
||||
{
|
||||
/// Save paths out using the PosixPathSeparator
|
||||
PathType path(reinterpret_cast<const PathType*>(classPtr)->Native(), AZ::IO::PosixPathSeparator);
|
||||
path.MakePreferred();
|
||||
|
||||
return static_cast<size_t>(stream.Write(path.Native().size(), path.c_str()));
|
||||
auto posixPathString{ reinterpret_cast<const PathType*>(classPtr)->AsPosix() };
|
||||
return static_cast<size_t>(stream.Write(posixPathString.size(), posixPathString.c_str()));
|
||||
}
|
||||
|
||||
bool Load(void* classPtr, IO::GenericStream& stream, unsigned int, bool) override
|
||||
@@ -73,5 +73,11 @@ namespace AZ::IO
|
||||
AZ::SerializeContext::IDataSerializer::CreateDefaultDeleteDeleter() })
|
||||
;
|
||||
}
|
||||
else if (auto jsonContext = azrtti_cast<JsonRegistrationContext*>(context))
|
||||
{
|
||||
jsonContext->Serializer<JsonPathSerializer>()
|
||||
->HandlesType<Path>()
|
||||
->HandlesType<FixedMaxPath>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,6 +109,7 @@ namespace AZ
|
||||
*/
|
||||
static EnvironmentVariable<T*> s_instance;
|
||||
static AZStd::shared_mutex s_mutex;
|
||||
static bool s_instanceAssigned;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
@@ -117,6 +118,9 @@ namespace AZ
|
||||
template <typename T>
|
||||
AZStd::shared_mutex Interface<T>::s_mutex;
|
||||
|
||||
template <typename T>
|
||||
bool Interface<T>::s_instanceAssigned;
|
||||
|
||||
template <typename T>
|
||||
void Interface<T>::Register(T* type)
|
||||
{
|
||||
@@ -135,18 +139,19 @@ namespace AZ
|
||||
AZStd::unique_lock<AZStd::shared_mutex> lock(s_mutex);
|
||||
s_instance = Environment::CreateVariable<T*>(GetVariableName());
|
||||
s_instance.Get() = type;
|
||||
s_instanceAssigned = true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Interface<T>::Unregister(T* type)
|
||||
{
|
||||
if (!s_instance || !s_instance.Get())
|
||||
if (!s_instanceAssigned)
|
||||
{
|
||||
AZ_Assert(false, "Interface '%s' not registered on this module!", AzTypeInfo<T>::Name());
|
||||
return;
|
||||
}
|
||||
|
||||
if (s_instance.Get() != type)
|
||||
if (s_instance && s_instance.Get() != type)
|
||||
{
|
||||
AZ_Assert(false, "Interface '%s' is not the same instance that was registered! [Expected '%p', Found '%p']", AzTypeInfo<T>::Name(), type, s_instance.Get());
|
||||
return;
|
||||
@@ -156,6 +161,7 @@ namespace AZ
|
||||
AZStd::unique_lock<AZStd::shared_mutex> lock(s_mutex);
|
||||
*s_instance = nullptr;
|
||||
s_instance.Reset();
|
||||
s_instanceAssigned = false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -165,9 +171,9 @@ namespace AZ
|
||||
// This is the fast path which won't block.
|
||||
{
|
||||
AZStd::shared_lock<AZStd::shared_mutex> lock(s_mutex);
|
||||
if (s_instance)
|
||||
if (s_instanceAssigned)
|
||||
{
|
||||
return s_instance.Get();
|
||||
return s_instance ? s_instance.Get() : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,6 +181,7 @@ namespace AZ
|
||||
// take the full lock and request it.
|
||||
AZStd::unique_lock<AZStd::shared_mutex> lock(s_mutex);
|
||||
s_instance = Environment::FindVariable<T*>(GetVariableName());
|
||||
s_instanceAssigned = true;
|
||||
return s_instance ? s_instance.Get() : nullptr;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,12 +19,14 @@ namespace AZ
|
||||
|
||||
AZ_MATH_INLINE Plane Plane::CreateFromNormalAndPoint(const Vector3& normal, const Vector3& point)
|
||||
{
|
||||
AZ_MATH_ASSERT(normal.IsNormalized(), "This normal is not normalized");
|
||||
return Plane(Simd::Vec4::ConstructPlane(normal.GetSimdValue(), point.GetSimdValue()));
|
||||
}
|
||||
|
||||
|
||||
AZ_MATH_INLINE Plane Plane::CreateFromNormalAndDistance(const Vector3& normal, float dist)
|
||||
{
|
||||
AZ_MATH_ASSERT(normal.IsNormalized(), "This normal is not normalized");
|
||||
Plane result;
|
||||
result.Set(normal, dist);
|
||||
return result;
|
||||
@@ -33,6 +35,7 @@ namespace AZ
|
||||
|
||||
AZ_MATH_INLINE Plane Plane::CreateFromCoefficients(const float a, const float b, const float c, const float d)
|
||||
{
|
||||
AZ_MATH_ASSERT(Vector3(a, b, c).IsNormalized(), "This normal is notormalized");
|
||||
Plane result;
|
||||
result.Set(a, b, c, d);
|
||||
return result;
|
||||
@@ -65,18 +68,21 @@ namespace AZ
|
||||
|
||||
AZ_MATH_INLINE void Plane::Set(const Vector3& normal, float d)
|
||||
{
|
||||
AZ_MATH_ASSERT(normal.IsNormalized(), "This normal is notormalized");
|
||||
m_plane.Set(normal, d);
|
||||
}
|
||||
|
||||
|
||||
AZ_MATH_INLINE void Plane::Set(float a, float b, float c, float d)
|
||||
{
|
||||
AZ_MATH_ASSERT(Vector3(a, b, c).IsNormalized(), "This normal is notormalized");
|
||||
m_plane.Set(a, b, c, d);
|
||||
}
|
||||
|
||||
|
||||
AZ_MATH_INLINE void Plane::SetNormal(const Vector3& normal)
|
||||
{
|
||||
AZ_MATH_ASSERT(normal.IsNormalized(), "This normal is notormalized");
|
||||
m_plane.SetX(normal.GetX());
|
||||
m_plane.SetY(normal.GetY());
|
||||
m_plane.SetZ(normal.GetZ());
|
||||
|
||||
@@ -254,13 +254,13 @@ namespace AZ
|
||||
Method("CreateFromMatrix3x3", &Quaternion::CreateFromMatrix3x3)->
|
||||
Method("CreateFromMatrix4x4", &Quaternion::CreateFromMatrix4x4)->
|
||||
Method("CreateFromAxisAngle", &Quaternion::CreateFromAxisAngle)->
|
||||
Method("CreateFromScaledAxisAngle", &Quaternion::CreateFromScaledAxisAngle)->
|
||||
Method("CreateShortestArc", &Quaternion::CreateShortestArc)->
|
||||
Method("CreateFromEulerAnglesDegrees", &Quaternion::CreateFromEulerAnglesDegrees)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Quaternion Quaternion::CreateFromMatrix3x3(const Matrix3x3& m)
|
||||
{
|
||||
return CreateFromBasis(m.GetBasisX(), m.GetBasisY(), m.GetBasisZ());
|
||||
@@ -430,4 +430,24 @@ namespace AZ
|
||||
outAngle = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vector3 Quaternion::ConvertToScaledAxisAngle() const
|
||||
{
|
||||
// Take the log of the quaternion to convert it to the exponential map
|
||||
// and multiply it by 2.0 to bring it into the scaled axis-angle representation.
|
||||
const AZ::Vector3 imaginary = GetImaginary();
|
||||
const float length = imaginary.GetLength();
|
||||
if (length < AZ::Constants::FloatEpsilon)
|
||||
{
|
||||
return imaginary * 2.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
const float halfAngle = acosf(AZ::GetClamp(GetW(), -1.0f, 1.0f));
|
||||
|
||||
// Multiply by 2.0 to convert the half angle into the full one.
|
||||
return halfAngle * 2.0f * (imaginary / length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,11 +54,11 @@ namespace AZ
|
||||
//! Sets components using a Vector3 for the imaginary part and a float for the real part.
|
||||
static Quaternion CreateFromVector3AndValue(const Vector3& v, float w);
|
||||
|
||||
//! Sets the quaternion to be a rotation around a specified axis.
|
||||
//! Sets the quaternion to be a rotation around a specified axis in radians.
|
||||
//! @{
|
||||
static Quaternion CreateRotationX(float angle);
|
||||
static Quaternion CreateRotationY(float angle);
|
||||
static Quaternion CreateRotationZ(float angle);
|
||||
static Quaternion CreateRotationX(float angleInRadians);
|
||||
static Quaternion CreateRotationY(float angleInRadians);
|
||||
static Quaternion CreateRotationZ(float angleInRadians);
|
||||
//! @}
|
||||
|
||||
//! Creates a quaternion from a Matrix3x3
|
||||
@@ -77,6 +77,9 @@ namespace AZ
|
||||
|
||||
static Quaternion CreateFromAxisAngle(const Vector3& axis, float angle);
|
||||
|
||||
//! Create a quaternion from a scaled axis-angle representation.
|
||||
static Quaternion CreateFromScaledAxisAngle(const Vector3& scaledAxisAngle);
|
||||
|
||||
static Quaternion CreateShortestArc(const Vector3& v1, const Vector3& v2);
|
||||
|
||||
//! Creates a quaternion using rotation in degrees about the axes. First rotated about the X axis, followed by the Y axis, then the Z axis.
|
||||
@@ -165,6 +168,14 @@ namespace AZ
|
||||
float NormalizeWithLengthEstimate();
|
||||
//! @}
|
||||
|
||||
//! Get the shortest equivalent of the rotation.
|
||||
//! In case the w component of the quaternion is negative the rotation is > 180° and taking the longer path.
|
||||
//! The quaternion will be inverted in that case to take the shortest path of rotation.
|
||||
//! @{
|
||||
Quaternion GetShortestEquivalent() const;
|
||||
void ShortestEquivalent();
|
||||
//! @}
|
||||
|
||||
//! Linearly interpolate towards a destination quaternion.
|
||||
//! @param[in] dest The quaternion to interpolate towards.
|
||||
//! @param[in] t Normalized interpolation value where 0.0 represents the current and 1.0 the destination value.
|
||||
@@ -231,6 +242,9 @@ namespace AZ
|
||||
//! @param[out] outAngle A float rotation angle around the axis in radians.
|
||||
void ConvertToAxisAngle(Vector3& outAxis, float& outAngle) const;
|
||||
|
||||
//! Convert the quaternion into scaled axis-angle representation.
|
||||
Vector3 ConvertToScaledAxisAngle() const;
|
||||
|
||||
//! Returns the imaginary (X/Y/Z) portion of the quaternion.
|
||||
Vector3 GetImaginary() const;
|
||||
|
||||
|
||||
@@ -73,27 +73,27 @@ namespace AZ
|
||||
}
|
||||
|
||||
|
||||
AZ_MATH_INLINE Quaternion Quaternion::CreateRotationX(float angle)
|
||||
AZ_MATH_INLINE Quaternion Quaternion::CreateRotationX(float angleInRadians)
|
||||
{
|
||||
const float halfAngle = 0.5f * angle;
|
||||
const float halfAngle = 0.5f * angleInRadians;
|
||||
float sin, cos;
|
||||
SinCos(halfAngle, sin, cos);
|
||||
return Quaternion(sin, 0.0f, 0.0f, cos);
|
||||
}
|
||||
|
||||
|
||||
AZ_MATH_INLINE Quaternion Quaternion::CreateRotationY(float angle)
|
||||
AZ_MATH_INLINE Quaternion Quaternion::CreateRotationY(float angleInRadians)
|
||||
{
|
||||
const float halfAngle = 0.5f * angle;
|
||||
const float halfAngle = 0.5f * angleInRadians;
|
||||
float sin, cos;
|
||||
SinCos(halfAngle, sin, cos);
|
||||
return Quaternion(0.0f, sin, 0.0f, cos);
|
||||
}
|
||||
|
||||
|
||||
AZ_MATH_INLINE Quaternion Quaternion::CreateRotationZ(float angle)
|
||||
AZ_MATH_INLINE Quaternion Quaternion::CreateRotationZ(float angleInRadians)
|
||||
{
|
||||
const float halfAngle = 0.5f * angle;
|
||||
const float halfAngle = 0.5f * angleInRadians;
|
||||
float sin, cos;
|
||||
SinCos(halfAngle, sin, cos);
|
||||
return Quaternion(0.0f, 0.0f, sin, cos);
|
||||
@@ -109,6 +109,24 @@ namespace AZ
|
||||
}
|
||||
|
||||
|
||||
AZ_MATH_INLINE Quaternion Quaternion::CreateFromScaledAxisAngle(const Vector3& scaledAxisAngle)
|
||||
{
|
||||
const AZ::Vector3 exponentialMap = scaledAxisAngle / 2.0f;
|
||||
const float halfAngle = exponentialMap.GetLength();
|
||||
|
||||
if (halfAngle < AZ::Constants::FloatEpsilon)
|
||||
{
|
||||
return AZ::Quaternion::CreateFromVector3AndValue(exponentialMap, 1.0f).GetNormalized();
|
||||
}
|
||||
else
|
||||
{
|
||||
float sin, cos;
|
||||
SinCos(halfAngle, sin, cos);
|
||||
return AZ::Quaternion::CreateFromVector3AndValue((sin / halfAngle) * exponentialMap, cos);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
AZ_MATH_INLINE void Quaternion::StoreToFloat4(float* values) const
|
||||
{
|
||||
Simd::Vec4::StoreUnaligned(values, m_value);
|
||||
@@ -327,6 +345,23 @@ namespace AZ
|
||||
}
|
||||
|
||||
|
||||
AZ_MATH_INLINE Quaternion Quaternion::GetShortestEquivalent() const
|
||||
{
|
||||
if (GetW() < 0.0f)
|
||||
{
|
||||
return -(*this);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
AZ_MATH_INLINE void Quaternion::ShortestEquivalent()
|
||||
{
|
||||
*this = GetShortestEquivalent();
|
||||
}
|
||||
|
||||
|
||||
AZ_MATH_INLINE Quaternion Quaternion::Lerp(const Quaternion& dest, float t) const
|
||||
{
|
||||
if (Dot(dest) >= 0.0f)
|
||||
|
||||
@@ -9,39 +9,38 @@
|
||||
#include <AzCore/Math/Sfmt.h>
|
||||
|
||||
#include <AzCore/Math/Random.h>
|
||||
#include <AzCore/std/parallel/lock.h>
|
||||
#include <AzCore/Module/Environment.h>
|
||||
#include <AzCore/std/parallel/lock.h>
|
||||
|
||||
#include <string.h> // for memset
|
||||
|
||||
namespace AZ::SfmtInternal
|
||||
{
|
||||
static const int N32 = N * 4;
|
||||
static const int N64 = N * 2;
|
||||
static const int POS1 = 122;
|
||||
static const int SL1 = 18;
|
||||
static const int SR1 = 11;
|
||||
static const int SL2 = 1;
|
||||
static const int SR2 = 1;
|
||||
static const unsigned int MSK1 = 0xdfffffefU;
|
||||
static const unsigned int MSK2 = 0xddfecb7fU;
|
||||
static const unsigned int MSK3 = 0xbffaffffU;
|
||||
static const unsigned int MSK4 = 0xbffffff6U;
|
||||
static const unsigned int PARITY1 = 0x00000001U;
|
||||
static const unsigned int PARITY2 = 0x00000000U;
|
||||
static const unsigned int PARITY3 = 0x00000000U;
|
||||
static const unsigned int PARITY4 = 0x13c9e684U;
|
||||
static const int N32 = N * 4;
|
||||
static const int N64 = N * 2;
|
||||
static const int POS1 = 122;
|
||||
static const int SL1 = 18;
|
||||
static const int SR1 = 11;
|
||||
static const int SL2 = 1;
|
||||
static const int SR2 = 1;
|
||||
static const unsigned int MSK1 = 0xdfffffefU;
|
||||
static const unsigned int MSK2 = 0xddfecb7fU;
|
||||
static const unsigned int MSK3 = 0xbffaffffU;
|
||||
static const unsigned int MSK4 = 0xbffffff6U;
|
||||
static const unsigned int PARITY1 = 0x00000001U;
|
||||
static const unsigned int PARITY2 = 0x00000000U;
|
||||
static const unsigned int PARITY3 = 0x00000000U;
|
||||
static const unsigned int PARITY4 = 0x13c9e684U;
|
||||
|
||||
/** a parity check vector which certificate the period of 2^{MEXP} */
|
||||
static unsigned int parity[4] = {PARITY1, PARITY2, PARITY3, PARITY4};
|
||||
static unsigned int parity[4] = { PARITY1, PARITY2, PARITY3, PARITY4 };
|
||||
|
||||
#ifdef ONLY64
|
||||
# define idxof(_i) (_i ^ 1)
|
||||
#define idxof(_i) (_i ^ 1)
|
||||
#else
|
||||
# define idxof(_i) _i
|
||||
#define idxof(_i) _i
|
||||
#endif // ONLY64
|
||||
|
||||
|
||||
#if AZ_TRAIT_USE_PLATFORM_SIMD_SSE
|
||||
/**
|
||||
* This function represents the recursion formula.
|
||||
@@ -52,7 +51,8 @@ namespace AZ::SfmtInternal
|
||||
* @param mask 128-bit mask
|
||||
* @return output
|
||||
*/
|
||||
AZ_FORCE_INLINE static Simd::Vec4::Int32Type simd_recursion(Simd::Vec4::Int32Type* a, Simd::Vec4::Int32Type* b, Simd::Vec4::Int32Type c, Simd::Vec4::Int32Type d, Simd::Vec4::Int32Type mask)
|
||||
AZ_FORCE_INLINE static Simd::Vec4::Int32Type simd_recursion(
|
||||
Simd::Vec4::Int32Type* a, Simd::Vec4::Int32Type* b, Simd::Vec4::Int32Type c, Simd::Vec4::Int32Type d, Simd::Vec4::Int32Type mask)
|
||||
{
|
||||
Simd::Vec4::Int32Type v, x, y, z;
|
||||
x = *a;
|
||||
@@ -151,7 +151,7 @@ namespace AZ::SfmtInternal
|
||||
inline void rshift128(w128_t* out, w128_t const* in, int shift)
|
||||
{
|
||||
AZ::u64 th, tl, oh, ol;
|
||||
#ifdef ONLY64
|
||||
#ifdef ONLY64
|
||||
th = ((AZ::u64)in->u[2] << 32) | ((AZ::u64)in->u[3]);
|
||||
tl = ((AZ::u64)in->u[0] << 32) | ((AZ::u64)in->u[1]);
|
||||
|
||||
@@ -204,7 +204,7 @@ namespace AZ::SfmtInternal
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void do_recursion(w128_t* r, w128_t* a, w128_t* b, w128_t* c, w128_t* d)
|
||||
inline void do_recursion(w128_t* r, w128_t* a, w128_t* b, w128_t* c, w128_t* d)
|
||||
{
|
||||
w128_t x;
|
||||
w128_t y;
|
||||
@@ -229,7 +229,7 @@ namespace AZ::SfmtInternal
|
||||
inline void gen_rand_all(Sfmt& g)
|
||||
{
|
||||
int i;
|
||||
w128_t* r1, * r2;
|
||||
w128_t *r1, *r2;
|
||||
|
||||
r1 = &g.m_sfmt[N - 2];
|
||||
r2 = &g.m_sfmt[N - 1];
|
||||
@@ -257,7 +257,7 @@ namespace AZ::SfmtInternal
|
||||
inline void gen_rand_array(Sfmt& g, w128_t* array, int size)
|
||||
{
|
||||
int i, j;
|
||||
w128_t* r1, * r2;
|
||||
w128_t *r1, *r2;
|
||||
|
||||
r1 = &g.m_sfmt[N - 2];
|
||||
r2 = &g.m_sfmt[N - 1];
|
||||
@@ -295,82 +295,80 @@ namespace AZ::SfmtInternal
|
||||
#endif
|
||||
} // namespace AZ::SfmtInternal
|
||||
|
||||
using namespace AZ;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Statics
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
static EnvironmentVariable<AZ::Sfmt> s_sfmt;
|
||||
static const char* s_globalSfmtName = "GlobalSfmt";
|
||||
|
||||
Sfmt& Sfmt::GetInstance()
|
||||
namespace AZ
|
||||
{
|
||||
if (!s_sfmt)
|
||||
static EnvironmentVariable<AZ::Sfmt> s_sfmt;
|
||||
static const char* s_globalSfmtName = "GlobalSfmt";
|
||||
|
||||
Sfmt& Sfmt::GetInstance()
|
||||
{
|
||||
s_sfmt = AZ::Environment::FindVariable<Sfmt>(s_globalSfmtName);
|
||||
if (!s_sfmt)
|
||||
{
|
||||
Sfmt::Create();
|
||||
s_sfmt = AZ::Environment::FindVariable<Sfmt>(s_globalSfmtName);
|
||||
if (!s_sfmt)
|
||||
{
|
||||
Sfmt::Create();
|
||||
}
|
||||
}
|
||||
|
||||
return s_sfmt.Get();
|
||||
}
|
||||
|
||||
void Sfmt::Create()
|
||||
{
|
||||
if (!s_sfmt)
|
||||
{
|
||||
s_sfmt = AZ::Environment::CreateVariable<AZ::Sfmt>(s_globalSfmtName);
|
||||
}
|
||||
}
|
||||
|
||||
return s_sfmt.Get();
|
||||
}
|
||||
|
||||
void Sfmt::Create()
|
||||
{
|
||||
if (!s_sfmt)
|
||||
void Sfmt::Destroy()
|
||||
{
|
||||
s_sfmt = AZ::Environment::CreateVariable<AZ::Sfmt>(s_globalSfmtName);
|
||||
s_sfmt.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void Sfmt::Destroy()
|
||||
{
|
||||
s_sfmt.Reset();
|
||||
}
|
||||
//=========================================================================
|
||||
// Sfmt
|
||||
// [4/10/2012]
|
||||
//=========================================================================
|
||||
Sfmt::Sfmt()
|
||||
{
|
||||
m_psfmt32 = &m_sfmt[0].u[0];
|
||||
m_psfmt64 = reinterpret_cast<AZ::u64*>(m_psfmt32);
|
||||
|
||||
//=========================================================================
|
||||
// Sfmt
|
||||
// [4/10/2012]
|
||||
//=========================================================================
|
||||
Sfmt::Sfmt()
|
||||
{
|
||||
m_psfmt32 = &m_sfmt[0].u[0];
|
||||
m_psfmt64 = reinterpret_cast<AZ::u64*>(m_psfmt32);
|
||||
Seed();
|
||||
}
|
||||
|
||||
Seed();
|
||||
}
|
||||
//=========================================================================
|
||||
// Seed
|
||||
// [4/10/2012]
|
||||
//=========================================================================
|
||||
Sfmt::Sfmt(AZ::u32* keys, int numKeys)
|
||||
{
|
||||
m_psfmt32 = &m_sfmt[0].u[0];
|
||||
m_psfmt64 = reinterpret_cast<AZ::u64*>(m_psfmt32);
|
||||
|
||||
//=========================================================================
|
||||
// Seed
|
||||
// [4/10/2012]
|
||||
//=========================================================================
|
||||
Sfmt::Sfmt(AZ::u32* keys, int numKeys)
|
||||
{
|
||||
m_psfmt32 = &m_sfmt[0].u[0];
|
||||
m_psfmt64 = reinterpret_cast<AZ::u64*>(m_psfmt32);
|
||||
Seed(keys, numKeys);
|
||||
}
|
||||
|
||||
Seed(keys, numKeys);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Seed
|
||||
// [4/10/2012]
|
||||
//=========================================================================
|
||||
void
|
||||
Sfmt::Seed()
|
||||
{
|
||||
// buffer with random values
|
||||
AZ::u32 buffer[32];
|
||||
BetterPseudoRandom rnd;
|
||||
bool result = rnd.GetRandom(buffer, sizeof(buffer));
|
||||
(void)result;
|
||||
AZ_Warning("System", result, "Failed to seed properly the Smft generator!");
|
||||
Seed(buffer, AZ_ARRAY_SIZE(buffer));
|
||||
}
|
||||
//=========================================================================
|
||||
// Seed
|
||||
// [4/10/2012]
|
||||
//=========================================================================
|
||||
void Sfmt::Seed()
|
||||
{
|
||||
// buffer with random values
|
||||
AZ::u32 buffer[32];
|
||||
BetterPseudoRandom rnd;
|
||||
bool result = rnd.GetRandom(buffer, sizeof(buffer));
|
||||
(void)result;
|
||||
AZ_Warning("System", result, "Failed to seed properly the Smft generator!");
|
||||
Seed(buffer, AZ_ARRAY_SIZE(buffer));
|
||||
}
|
||||
|
||||
/**
|
||||
* This function represents a function used in the initialization
|
||||
@@ -388,226 +386,222 @@ Sfmt::Seed()
|
||||
*/
|
||||
#define azsfmt_func2(x) ((x ^ (x >> 27)) * (AZ::u32)1566083941UL)
|
||||
|
||||
//=========================================================================
|
||||
// Seed
|
||||
// [4/10/2012]
|
||||
//=========================================================================
|
||||
void
|
||||
Sfmt::Seed(AZ::u32* keys, int numKeys)
|
||||
{
|
||||
using SfmtInternal::N;
|
||||
using SfmtInternal::N32;
|
||||
int i, j, count;
|
||||
AZ::u32 r;
|
||||
int lag;
|
||||
int mid;
|
||||
int size = N * 4;
|
||||
//=========================================================================
|
||||
// Seed
|
||||
// [4/10/2012]
|
||||
//=========================================================================
|
||||
void Sfmt::Seed(AZ::u32* keys, int numKeys)
|
||||
{
|
||||
using SfmtInternal::N;
|
||||
using SfmtInternal::N32;
|
||||
int i, j, count;
|
||||
AZ::u32 r;
|
||||
int lag;
|
||||
int mid;
|
||||
int size = N * 4;
|
||||
|
||||
if (size >= 623)
|
||||
{
|
||||
lag = 11;
|
||||
}
|
||||
else if (size >= 68)
|
||||
{
|
||||
lag = 7;
|
||||
}
|
||||
else if (size >= 39)
|
||||
{
|
||||
lag = 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
lag = 3;
|
||||
}
|
||||
mid = (size - lag) / 2;
|
||||
if (size >= 623)
|
||||
{
|
||||
lag = 11;
|
||||
}
|
||||
else if (size >= 68)
|
||||
{
|
||||
lag = 7;
|
||||
}
|
||||
else if (size >= 39)
|
||||
{
|
||||
lag = 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
lag = 3;
|
||||
}
|
||||
mid = (size - lag) / 2;
|
||||
|
||||
memset(m_sfmt, 0x8b, sizeof(m_sfmt));
|
||||
if (numKeys + 1 > SfmtInternal::N32)
|
||||
{
|
||||
count = numKeys + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
count = N32;
|
||||
}
|
||||
r = azsfmt_func1((m_psfmt32[idxof(0)] ^ m_psfmt32[idxof(mid)] ^ m_psfmt32[idxof(N32 - 1)]));
|
||||
m_psfmt32[idxof(mid)] += r;
|
||||
r += numKeys;
|
||||
m_psfmt32[idxof(mid + lag)] += r;
|
||||
m_psfmt32[idxof(0)] = r;
|
||||
memset(m_sfmt, 0x8b, sizeof(m_sfmt));
|
||||
if (numKeys + 1 > SfmtInternal::N32)
|
||||
{
|
||||
count = numKeys + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
count = N32;
|
||||
}
|
||||
r = azsfmt_func1((m_psfmt32[idxof(0)] ^ m_psfmt32[idxof(mid)] ^ m_psfmt32[idxof(N32 - 1)]));
|
||||
m_psfmt32[idxof(mid)] += r;
|
||||
r += numKeys;
|
||||
m_psfmt32[idxof(mid + lag)] += r;
|
||||
m_psfmt32[idxof(0)] = r;
|
||||
|
||||
count--;
|
||||
for (i = 1, j = 0; (j < count) && (j < numKeys); j++)
|
||||
{
|
||||
r = azsfmt_func1((m_psfmt32[idxof(i)] ^ m_psfmt32[idxof((i + mid) % N32)] ^ m_psfmt32[idxof((i + N32 - 1) % N32)]));
|
||||
m_psfmt32[idxof((i + mid) % N32)] += r;
|
||||
r += keys[j] + i;
|
||||
m_psfmt32[idxof((i + mid + lag) % N32)] += r;
|
||||
m_psfmt32[idxof(i)] = r;
|
||||
i = (i + 1) % N32;
|
||||
}
|
||||
for (; j < count; j++)
|
||||
{
|
||||
r = azsfmt_func1((m_psfmt32[idxof(i)] ^ m_psfmt32[idxof((i + mid) % N32)] ^ m_psfmt32[idxof((i + N32 - 1) % N32)]));
|
||||
m_psfmt32[idxof((i + mid) % N32)] += r;
|
||||
r += i;
|
||||
m_psfmt32[idxof((i + mid + lag) % N32)] += r;
|
||||
m_psfmt32[idxof(i)] = r;
|
||||
i = (i + 1) % N32;
|
||||
}
|
||||
for (j = 0; j < N32; j++)
|
||||
{
|
||||
r = azsfmt_func2((m_psfmt32[idxof(i)] + m_psfmt32[idxof((i + mid) % N32)] + m_psfmt32[idxof((i + N32 - 1) % N32)]));
|
||||
m_psfmt32[idxof((i + mid) % N32)] ^= r;
|
||||
r -= i;
|
||||
m_psfmt32[idxof((i + mid + lag) % N32)] ^= r;
|
||||
m_psfmt32[idxof(i)] = r;
|
||||
i = (i + 1) % N32;
|
||||
}
|
||||
count--;
|
||||
for (i = 1, j = 0; (j < count) && (j < numKeys); j++)
|
||||
{
|
||||
r = azsfmt_func1((m_psfmt32[idxof(i)] ^ m_psfmt32[idxof((i + mid) % N32)] ^ m_psfmt32[idxof((i + N32 - 1) % N32)]));
|
||||
m_psfmt32[idxof((i + mid) % N32)] += r;
|
||||
r += keys[j] + i;
|
||||
m_psfmt32[idxof((i + mid + lag) % N32)] += r;
|
||||
m_psfmt32[idxof(i)] = r;
|
||||
i = (i + 1) % N32;
|
||||
}
|
||||
for (; j < count; j++)
|
||||
{
|
||||
r = azsfmt_func1((m_psfmt32[idxof(i)] ^ m_psfmt32[idxof((i + mid) % N32)] ^ m_psfmt32[idxof((i + N32 - 1) % N32)]));
|
||||
m_psfmt32[idxof((i + mid) % N32)] += r;
|
||||
r += i;
|
||||
m_psfmt32[idxof((i + mid + lag) % N32)] += r;
|
||||
m_psfmt32[idxof(i)] = r;
|
||||
i = (i + 1) % N32;
|
||||
}
|
||||
for (j = 0; j < N32; j++)
|
||||
{
|
||||
r = azsfmt_func2((m_psfmt32[idxof(i)] + m_psfmt32[idxof((i + mid) % N32)] + m_psfmt32[idxof((i + N32 - 1) % N32)]));
|
||||
m_psfmt32[idxof((i + mid) % N32)] ^= r;
|
||||
r -= i;
|
||||
m_psfmt32[idxof((i + mid + lag) % N32)] ^= r;
|
||||
m_psfmt32[idxof(i)] = r;
|
||||
i = (i + 1) % N32;
|
||||
}
|
||||
|
||||
m_index = N32;
|
||||
PeriodCertification();
|
||||
}
|
||||
m_index = N32;
|
||||
PeriodCertification();
|
||||
}
|
||||
|
||||
#undef azsfmt_func1
|
||||
#undef azsfmt_func2
|
||||
|
||||
//=========================================================================
|
||||
// PeriodCertification
|
||||
// [4/10/2012]
|
||||
//=========================================================================
|
||||
void
|
||||
Sfmt::PeriodCertification()
|
||||
{
|
||||
int inner = 0;
|
||||
int i, j;
|
||||
AZ::u32 work;
|
||||
//=========================================================================
|
||||
// PeriodCertification
|
||||
// [4/10/2012]
|
||||
//=========================================================================
|
||||
void Sfmt::PeriodCertification()
|
||||
{
|
||||
int inner = 0;
|
||||
int i, j;
|
||||
AZ::u32 work;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
inner ^= m_psfmt32[idxof(i)] & SfmtInternal::parity[i];
|
||||
}
|
||||
for (i = 16; i > 0; i >>= 1)
|
||||
{
|
||||
inner ^= inner >> i;
|
||||
}
|
||||
inner &= 1;
|
||||
/* check OK */
|
||||
if (inner == 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
/* check NG, and modification */
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
work = 1;
|
||||
for (j = 0; j < 32; j++)
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
if ((work & SfmtInternal::parity[i]) != 0)
|
||||
inner ^= m_psfmt32[idxof(i)] & SfmtInternal::parity[i];
|
||||
}
|
||||
for (i = 16; i > 0; i >>= 1)
|
||||
{
|
||||
inner ^= inner >> i;
|
||||
}
|
||||
inner &= 1;
|
||||
/* check OK */
|
||||
if (inner == 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
/* check NG, and modification */
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
work = 1;
|
||||
for (j = 0; j < 32; j++)
|
||||
{
|
||||
m_psfmt32[idxof(i)] ^= work;
|
||||
return;
|
||||
if ((work & SfmtInternal::parity[i]) != 0)
|
||||
{
|
||||
m_psfmt32[idxof(i)] ^= work;
|
||||
return;
|
||||
}
|
||||
work = work << 1;
|
||||
}
|
||||
work = work << 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Rand32
|
||||
// [4/10/2012]
|
||||
//=========================================================================
|
||||
AZ::u32 Sfmt::Rand32()
|
||||
{
|
||||
int index = m_index.fetch_add(1);
|
||||
if (index >= SfmtInternal::N32)
|
||||
//=========================================================================
|
||||
// Rand32
|
||||
// [4/10/2012]
|
||||
//=========================================================================
|
||||
AZ::u32 Sfmt::Rand32()
|
||||
{
|
||||
AZStd::lock_guard<decltype(m_generationMutex)> lock(m_generationMutex);
|
||||
// if this thread is the one that sets m_index to 0, then this thread
|
||||
// does the generation
|
||||
index += 1; // compare against the result of fetch_add(1) above
|
||||
if (m_index.compare_exchange_strong(index, 0))
|
||||
int index = m_index.fetch_add(1);
|
||||
if (index >= SfmtInternal::N32)
|
||||
{
|
||||
SfmtInternal::gen_rand_all(*this);
|
||||
AZStd::lock_guard<decltype(m_generationMutex)> lock(m_generationMutex);
|
||||
// if this thread is the one that sets m_index to 0, then this thread
|
||||
// does the generation
|
||||
index += 1; // compare against the result of fetch_add(1) above
|
||||
if (m_index.compare_exchange_strong(index, 0))
|
||||
{
|
||||
SfmtInternal::gen_rand_all(*this);
|
||||
}
|
||||
// try again, with the new table
|
||||
return Rand32();
|
||||
}
|
||||
// try again, with the new table
|
||||
return Rand32();
|
||||
return m_psfmt32[index];
|
||||
}
|
||||
return m_psfmt32[index];
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Rand64
|
||||
// [4/10/2012]
|
||||
//=========================================================================
|
||||
AZ::u64 Sfmt::Rand64()
|
||||
{
|
||||
int index = m_index.fetch_add(2);
|
||||
if (index >= (SfmtInternal::N32 - 1))
|
||||
//=========================================================================
|
||||
// Rand64
|
||||
// [4/10/2012]
|
||||
//=========================================================================
|
||||
AZ::u64 Sfmt::Rand64()
|
||||
{
|
||||
AZStd::lock_guard<decltype(m_generationMutex)> lock(m_generationMutex);
|
||||
// if this thread is the one that sets m_index to 0, then this thread
|
||||
// does the generation
|
||||
index += 2; // compare against the result of fetch_add(2) above
|
||||
if (m_index.compare_exchange_strong(index, 0))
|
||||
int index = m_index.fetch_add(2);
|
||||
if (index >= (SfmtInternal::N32 - 1))
|
||||
{
|
||||
SfmtInternal::gen_rand_all(*this);
|
||||
AZStd::lock_guard<decltype(m_generationMutex)> lock(m_generationMutex);
|
||||
// if this thread is the one that sets m_index to 0, then this thread
|
||||
// does the generation
|
||||
index += 2; // compare against the result of fetch_add(2) above
|
||||
if (m_index.compare_exchange_strong(index, 0))
|
||||
{
|
||||
SfmtInternal::gen_rand_all(*this);
|
||||
}
|
||||
// try again, with the new table
|
||||
return Rand64();
|
||||
}
|
||||
// try again, with the new table
|
||||
return Rand64();
|
||||
|
||||
AZ::u64 r;
|
||||
r = m_psfmt64[index / 2];
|
||||
return r;
|
||||
}
|
||||
|
||||
AZ::u64 r;
|
||||
r = m_psfmt64[index / 2];
|
||||
return r;
|
||||
}
|
||||
//=========================================================================
|
||||
// FillArray32
|
||||
// [4/10/2012]
|
||||
//=========================================================================
|
||||
void Sfmt::FillArray32(AZ::u32* array, int size)
|
||||
{
|
||||
AZ_MATH_ASSERT(m_index == SfmtInternal::N32, "Invalid m_index! Reinitialize!");
|
||||
AZ_MATH_ASSERT(size % 4 == 0, "Size must be multiple of 4!");
|
||||
AZ_MATH_ASSERT(size >= SfmtInternal::N32, "Size must be bigger than %d GetMinArray32Size()!", SfmtInternal::N32);
|
||||
|
||||
//=========================================================================
|
||||
// FillArray32
|
||||
// [4/10/2012]
|
||||
//=========================================================================
|
||||
void
|
||||
Sfmt::FillArray32(AZ::u32* array, int size)
|
||||
{
|
||||
AZ_MATH_ASSERT(m_index == SfmtInternal::N32, "Invalid m_index! Reinitialize!");
|
||||
AZ_MATH_ASSERT(size % 4 == 0, "Size must be multiple of 4!");
|
||||
AZ_MATH_ASSERT(size >= SfmtInternal::N32, "Size must be bigger than %d GetMinArray32Size()!", SfmtInternal::N32);
|
||||
SfmtInternal::gen_rand_array(*this, (SfmtInternal::w128_t*)array, size / 4);
|
||||
m_index = SfmtInternal::N32;
|
||||
}
|
||||
|
||||
SfmtInternal::gen_rand_array(*this, (SfmtInternal::w128_t*)array, size / 4);
|
||||
m_index = SfmtInternal::N32;
|
||||
}
|
||||
//=========================================================================
|
||||
// FillArray64
|
||||
// [4/10/2012]
|
||||
//=========================================================================
|
||||
void Sfmt::FillArray64(AZ::u64* array, int size)
|
||||
{
|
||||
AZ_MATH_ASSERT(m_index == SfmtInternal::N32, "Invalid m_index! Reinitialize!");
|
||||
AZ_MATH_ASSERT(size % 4 == 0, "Size must be multiple of 4!");
|
||||
AZ_MATH_ASSERT(size >= SfmtInternal::N64, "Size must be bigger than %d GetMinArray64Size()!", SfmtInternal::N64);
|
||||
|
||||
//=========================================================================
|
||||
// FillArray64
|
||||
// [4/10/2012]
|
||||
//=========================================================================
|
||||
void
|
||||
Sfmt::FillArray64(AZ::u64* array, int size)
|
||||
{
|
||||
AZ_MATH_ASSERT(m_index == SfmtInternal::N32, "Invalid m_index! Reinitialize!");
|
||||
AZ_MATH_ASSERT(size % 4 == 0, "Size must be multiple of 4!");
|
||||
AZ_MATH_ASSERT(size >= SfmtInternal::N64, "Size must be bigger than %d GetMinArray64Size()!", SfmtInternal::N64);
|
||||
SfmtInternal::gen_rand_array(*this, (SfmtInternal::w128_t*)array, size / 2);
|
||||
m_index = SfmtInternal::N32;
|
||||
}
|
||||
|
||||
SfmtInternal::gen_rand_array(*this, (SfmtInternal::w128_t*)array, size / 2);
|
||||
m_index = SfmtInternal::N32;
|
||||
}
|
||||
//=========================================================================
|
||||
// GetMinArray32Size
|
||||
// [4/10/2012]
|
||||
//=========================================================================
|
||||
int Sfmt::GetMinArray32Size() const
|
||||
{
|
||||
return SfmtInternal::N32;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// GetMinArray32Size
|
||||
// [4/10/2012]
|
||||
//=========================================================================
|
||||
int
|
||||
Sfmt::GetMinArray32Size() const
|
||||
{
|
||||
return SfmtInternal::N32;
|
||||
}
|
||||
//=========================================================================
|
||||
// GetMinArray64Size
|
||||
// [4/10/2012]
|
||||
//=========================================================================
|
||||
int Sfmt::GetMinArray64Size() const
|
||||
{
|
||||
return SfmtInternal::N64;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// GetMinArray64Size
|
||||
// [4/10/2012]
|
||||
//=========================================================================
|
||||
int
|
||||
Sfmt::GetMinArray64Size() const
|
||||
{
|
||||
return SfmtInternal::N64;
|
||||
}
|
||||
} // namespace AZ
|
||||
|
||||
@@ -16,438 +16,421 @@
|
||||
|
||||
#include <AzCore/Debug/StackTracer.h>
|
||||
|
||||
using namespace AZ;
|
||||
using namespace AZ::Debug;
|
||||
namespace AZ::Debug
|
||||
{
|
||||
// Many PC tools break with alloc/free size mismatches when the memory guard is enabled. Disable for now
|
||||
//#define ENABLE_MEMORY_GUARD
|
||||
|
||||
// Many PC tools break with alloc/free size mismatches when the memory guard is enabled. Disable for now
|
||||
//#define ENABLE_MEMORY_GUARD
|
||||
|
||||
//=========================================================================
|
||||
// AllocationRecords
|
||||
// [9/16/2009]
|
||||
//=========================================================================
|
||||
AllocationRecords::AllocationRecords(unsigned char stackRecordLevels, [[maybe_unused]] bool isMemoryGuard, bool isMarkUnallocatedMemory, const char* allocatorName)
|
||||
: m_mode(AllocatorManager::Instance().m_defaultTrackingRecordMode)
|
||||
, m_isAutoIntegrityCheck(false)
|
||||
, m_isMarkUnallocatedMemory(isMarkUnallocatedMemory)
|
||||
, m_saveNames(false)
|
||||
, m_decodeImmediately(false)
|
||||
, m_numStackLevels(stackRecordLevels)
|
||||
//=========================================================================
|
||||
// AllocationRecords
|
||||
// [9/16/2009]
|
||||
//=========================================================================
|
||||
AllocationRecords::AllocationRecords(
|
||||
unsigned char stackRecordLevels, [[maybe_unused]] bool isMemoryGuard, bool isMarkUnallocatedMemory, const char* allocatorName)
|
||||
: m_mode(AllocatorManager::Instance().m_defaultTrackingRecordMode)
|
||||
, m_isAutoIntegrityCheck(false)
|
||||
, m_isMarkUnallocatedMemory(isMarkUnallocatedMemory)
|
||||
, m_saveNames(false)
|
||||
, m_decodeImmediately(false)
|
||||
, m_numStackLevels(stackRecordLevels)
|
||||
#if defined(ENABLE_MEMORY_GUARD)
|
||||
, m_memoryGuardSize(isMemoryGuard ? sizeof(Debug::GuardValue) : 0)
|
||||
, m_memoryGuardSize(isMemoryGuard ? sizeof(Debug::GuardValue) : 0)
|
||||
#else
|
||||
, m_memoryGuardSize(0)
|
||||
, m_memoryGuardSize(0)
|
||||
#endif
|
||||
, m_requestedAllocs(0)
|
||||
, m_requestedBytes(0)
|
||||
, m_requestedBytesPeak(0)
|
||||
, m_allocatorName(allocatorName)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// ~AllocationRecords
|
||||
// [9/16/2009]
|
||||
//=========================================================================
|
||||
AllocationRecords::~AllocationRecords()
|
||||
{
|
||||
if (!AllocatorManager::Instance().m_isAllocatorLeaking)
|
||||
, m_requestedAllocs(0)
|
||||
, m_requestedBytes(0)
|
||||
, m_requestedBytesPeak(0)
|
||||
, m_allocatorName(allocatorName)
|
||||
{
|
||||
// dump all allocation (we should not have any at this point).
|
||||
bool includeNameAndFilename = (m_saveNames || m_mode == RECORD_FULL);
|
||||
EnumerateAllocations(PrintAllocationsCB(true, includeNameAndFilename));
|
||||
AZ_Error("Memory", m_records.empty(), "We still have %d allocations on record! They must be freed prior to destroy!", m_records.size());
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// lock
|
||||
// [9/16/2009]
|
||||
//=========================================================================
|
||||
void
|
||||
AllocationRecords::lock()
|
||||
{
|
||||
m_recordsMutex.lock();
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// try_lock
|
||||
// [9/16/2009]
|
||||
//=========================================================================
|
||||
bool AllocationRecords::try_lock()
|
||||
{
|
||||
return m_recordsMutex.try_lock();
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// unlock
|
||||
// [9/16/2009]
|
||||
//=========================================================================
|
||||
void
|
||||
AllocationRecords::unlock()
|
||||
{
|
||||
m_recordsMutex.unlock();
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// RegisterAllocation
|
||||
// [9/11/2009]
|
||||
//=========================================================================
|
||||
const AllocationInfo*
|
||||
AllocationRecords::RegisterAllocation(void* address, size_t byteSize, size_t alignment, const char* name, const char* fileName, int lineNum, unsigned int stackSuppressCount)
|
||||
{
|
||||
(void)stackSuppressCount;
|
||||
if (m_mode == RECORD_NO_RECORDS)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
if (address == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// memory guard
|
||||
if (m_memoryGuardSize == sizeof(Debug::GuardValue))
|
||||
//=========================================================================
|
||||
// ~AllocationRecords
|
||||
// [9/16/2009]
|
||||
//=========================================================================
|
||||
AllocationRecords::~AllocationRecords()
|
||||
{
|
||||
if (m_isAutoIntegrityCheck)
|
||||
if (!AllocatorManager::Instance().m_isAllocatorLeaking)
|
||||
{
|
||||
IntegrityCheck();
|
||||
// dump all allocation (we should not have any at this point).
|
||||
bool includeNameAndFilename = (m_saveNames || m_mode == RECORD_FULL);
|
||||
EnumerateAllocations(PrintAllocationsCB(true, includeNameAndFilename));
|
||||
AZ_Error(
|
||||
"Memory", m_records.empty(), "We still have %d allocations on record! They must be freed prior to destroy!",
|
||||
m_records.size());
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// lock
|
||||
// [9/16/2009]
|
||||
//=========================================================================
|
||||
void AllocationRecords::lock()
|
||||
{
|
||||
m_recordsMutex.lock();
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// try_lock
|
||||
// [9/16/2009]
|
||||
//=========================================================================
|
||||
bool AllocationRecords::try_lock()
|
||||
{
|
||||
return m_recordsMutex.try_lock();
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// unlock
|
||||
// [9/16/2009]
|
||||
//=========================================================================
|
||||
void AllocationRecords::unlock()
|
||||
{
|
||||
m_recordsMutex.unlock();
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// RegisterAllocation
|
||||
// [9/11/2009]
|
||||
//=========================================================================
|
||||
const AllocationInfo* AllocationRecords::RegisterAllocation(
|
||||
void* address,
|
||||
size_t byteSize,
|
||||
size_t alignment,
|
||||
const char* name,
|
||||
const char* fileName,
|
||||
int lineNum,
|
||||
unsigned int stackSuppressCount)
|
||||
{
|
||||
(void)stackSuppressCount;
|
||||
if (m_mode == RECORD_NO_RECORDS)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
if (address == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AZ_Assert(byteSize>sizeof(Debug::GuardValue), "Did you forget to add the extra MemoryGuardSize() bytes?");
|
||||
byteSize -= sizeof(Debug::GuardValue);
|
||||
new(reinterpret_cast<char*>(address)+byteSize) Debug::GuardValue();
|
||||
}
|
||||
|
||||
Debug::AllocationRecordsType::pair_iter_bool iterBool;
|
||||
{
|
||||
AZStd::scoped_lock lock(m_recordsMutex);
|
||||
iterBool = m_records.insert_key(address);
|
||||
}
|
||||
|
||||
if (!iterBool.second)
|
||||
{
|
||||
// If that memory address was already registered, print the stack trace of the previous registration
|
||||
PrintAllocationsCB(true, (m_saveNames || m_mode == RECORD_FULL))(address, iterBool.first->second, m_numStackLevels);
|
||||
AZ_Assert(iterBool.second, "Memory address 0x%p is already allocated and in the records!", address);
|
||||
}
|
||||
|
||||
Debug::AllocationInfo& ai = iterBool.first->second;
|
||||
ai.m_byteSize = byteSize;
|
||||
ai.m_alignment = static_cast<unsigned int>(alignment);
|
||||
if ((m_saveNames || m_mode == RECORD_FULL) && name && fileName)
|
||||
{
|
||||
// In RECORD_FULL mode or when specifically enabled in app descriptor with
|
||||
// m_allocationRecordsSaveNames, we allocate our own memory to save off name and fileName.
|
||||
// When testing for memory leaks, on process shutdown AllocationRecords::~AllocationRecords
|
||||
// gets called to enumerate the remaining (leaked) allocations. Unfortunately, any names
|
||||
// referenced in dynamic module memory whose modules are unloaded won't be valid
|
||||
// references anymore and we won't get useful information from the enumeration print.
|
||||
// This code block ensures we keep our name/fileName valid for when we need it.
|
||||
const size_t nameLength = strlen(name);
|
||||
const size_t fileNameLength = strlen(fileName);
|
||||
const size_t totalLength = nameLength + fileNameLength + 2; // + 2 for terminating null characters
|
||||
ai.m_namesBlock = m_records.get_allocator().allocate(totalLength, 1);
|
||||
ai.m_namesBlockSize = totalLength;
|
||||
char* savedName = reinterpret_cast<char*>(ai.m_namesBlock);
|
||||
char* savedFileName = savedName + nameLength + 1;
|
||||
memcpy(reinterpret_cast<void*>(savedName), reinterpret_cast<const void*>(name), nameLength + 1);
|
||||
memcpy(reinterpret_cast<void*>(savedFileName), reinterpret_cast<const void*>(fileName), fileNameLength + 1);
|
||||
ai.m_name = savedName;
|
||||
ai.m_fileName = savedFileName;
|
||||
}
|
||||
else
|
||||
{
|
||||
ai.m_name = name;
|
||||
ai.m_fileName = fileName;
|
||||
ai.m_namesBlock = nullptr;
|
||||
ai.m_namesBlockSize = 0;
|
||||
}
|
||||
ai.m_lineNum = lineNum;
|
||||
ai.m_timeStamp = AZStd::GetTimeNowMicroSecond();
|
||||
|
||||
// if we don't have a fileName,lineNum record the stack or if the user requested it.
|
||||
if ((fileName == nullptr && m_mode == RECORD_STACK_IF_NO_FILE_LINE) || m_mode == RECORD_FULL)
|
||||
{
|
||||
ai.m_stackFrames = m_numStackLevels ? reinterpret_cast<AZ::Debug::StackFrame*>(m_records.get_allocator().allocate(sizeof(AZ::Debug::StackFrame)*m_numStackLevels, 1)) : nullptr;
|
||||
if (ai.m_stackFrames)
|
||||
// memory guard
|
||||
if (m_memoryGuardSize == sizeof(Debug::GuardValue))
|
||||
{
|
||||
Debug::StackRecorder::Record(ai.m_stackFrames, m_numStackLevels, stackSuppressCount + 1);
|
||||
|
||||
if (m_decodeImmediately)
|
||||
if (m_isAutoIntegrityCheck)
|
||||
{
|
||||
// OPTIONAL DEBUGGING CODE - enable in app descriptor m_allocationRecordsAttemptDecodeImmediately
|
||||
// This is optionally-enabled code for tracking down memory allocations
|
||||
// that fail to be decoded. DecodeFrames() typically runs at the end of
|
||||
// your application when leaks were found. Sometimes you have stack prints
|
||||
// full of "(module-name not available)" and "(function-name not available)"
|
||||
// that are not actionable. If you have those, enable this code. It'll slow
|
||||
// down your process significantly because for every allocation recorded
|
||||
// we get the stack trace on the spot. Put a breakpoint in DecodeFrames()
|
||||
// at the "(module-name not available)" and "(function-name not available)"
|
||||
// locations and now at the moment those allocations happen you'll have the
|
||||
// full stack trace available and the ability to debug what could be causing it
|
||||
IntegrityCheck();
|
||||
}
|
||||
|
||||
AZ_Assert(byteSize > sizeof(Debug::GuardValue), "Did you forget to add the extra MemoryGuardSize() bytes?");
|
||||
byteSize -= sizeof(Debug::GuardValue);
|
||||
new (reinterpret_cast<char*>(address) + byteSize) Debug::GuardValue();
|
||||
}
|
||||
|
||||
Debug::AllocationRecordsType::pair_iter_bool iterBool;
|
||||
{
|
||||
AZStd::scoped_lock lock(m_recordsMutex);
|
||||
iterBool = m_records.insert_key(address);
|
||||
}
|
||||
|
||||
if (!iterBool.second)
|
||||
{
|
||||
// If that memory address was already registered, print the stack trace of the previous registration
|
||||
PrintAllocationsCB(true, (m_saveNames || m_mode == RECORD_FULL))(address, iterBool.first->second, m_numStackLevels);
|
||||
AZ_Assert(iterBool.second, "Memory address 0x%p is already allocated and in the records!", address);
|
||||
}
|
||||
|
||||
Debug::AllocationInfo& ai = iterBool.first->second;
|
||||
ai.m_byteSize = byteSize;
|
||||
ai.m_alignment = static_cast<unsigned int>(alignment);
|
||||
if ((m_saveNames || m_mode == RECORD_FULL) && name && fileName)
|
||||
{
|
||||
// In RECORD_FULL mode or when specifically enabled in app descriptor with
|
||||
// m_allocationRecordsSaveNames, we allocate our own memory to save off name and fileName.
|
||||
// When testing for memory leaks, on process shutdown AllocationRecords::~AllocationRecords
|
||||
// gets called to enumerate the remaining (leaked) allocations. Unfortunately, any names
|
||||
// referenced in dynamic module memory whose modules are unloaded won't be valid
|
||||
// references anymore and we won't get useful information from the enumeration print.
|
||||
// This code block ensures we keep our name/fileName valid for when we need it.
|
||||
const size_t nameLength = strlen(name);
|
||||
const size_t fileNameLength = strlen(fileName);
|
||||
const size_t totalLength = nameLength + fileNameLength + 2; // + 2 for terminating null characters
|
||||
ai.m_namesBlock = m_records.get_allocator().allocate(totalLength, 1);
|
||||
ai.m_namesBlockSize = totalLength;
|
||||
char* savedName = reinterpret_cast<char*>(ai.m_namesBlock);
|
||||
char* savedFileName = savedName + nameLength + 1;
|
||||
memcpy(reinterpret_cast<void*>(savedName), reinterpret_cast<const void*>(name), nameLength + 1);
|
||||
memcpy(reinterpret_cast<void*>(savedFileName), reinterpret_cast<const void*>(fileName), fileNameLength + 1);
|
||||
ai.m_name = savedName;
|
||||
ai.m_fileName = savedFileName;
|
||||
}
|
||||
else
|
||||
{
|
||||
ai.m_name = name;
|
||||
ai.m_fileName = fileName;
|
||||
ai.m_namesBlock = nullptr;
|
||||
ai.m_namesBlockSize = 0;
|
||||
}
|
||||
ai.m_lineNum = lineNum;
|
||||
ai.m_timeStamp = AZStd::GetTimeNowMicroSecond();
|
||||
|
||||
// if we don't have a fileName,lineNum record the stack or if the user requested it.
|
||||
if ((fileName == nullptr && m_mode == RECORD_STACK_IF_NO_FILE_LINE) || m_mode == RECORD_FULL)
|
||||
{
|
||||
ai.m_stackFrames = m_numStackLevels ? reinterpret_cast<AZ::Debug::StackFrame*>(m_records.get_allocator().allocate(
|
||||
sizeof(AZ::Debug::StackFrame) * m_numStackLevels, 1))
|
||||
: nullptr;
|
||||
if (ai.m_stackFrames)
|
||||
{
|
||||
Debug::StackRecorder::Record(ai.m_stackFrames, m_numStackLevels, stackSuppressCount + 1);
|
||||
|
||||
if (m_decodeImmediately)
|
||||
{
|
||||
const unsigned char decodeStep = 40;
|
||||
Debug::SymbolStorage::StackLine lines[decodeStep];
|
||||
unsigned char iFrame = 0;
|
||||
unsigned char numStackLevels = m_numStackLevels;
|
||||
while (numStackLevels > 0)
|
||||
// OPTIONAL DEBUGGING CODE - enable in app descriptor m_allocationRecordsAttemptDecodeImmediately
|
||||
// This is optionally-enabled code for tracking down memory allocations
|
||||
// that fail to be decoded. DecodeFrames() typically runs at the end of
|
||||
// your application when leaks were found. Sometimes you have stack prints
|
||||
// full of "(module-name not available)" and "(function-name not available)"
|
||||
// that are not actionable. If you have those, enable this code. It'll slow
|
||||
// down your process significantly because for every allocation recorded
|
||||
// we get the stack trace on the spot. Put a breakpoint in DecodeFrames()
|
||||
// at the "(module-name not available)" and "(function-name not available)"
|
||||
// locations and now at the moment those allocations happen you'll have the
|
||||
// full stack trace available and the ability to debug what could be causing it
|
||||
{
|
||||
unsigned char numToDecode = AZStd::GetMin(decodeStep, numStackLevels);
|
||||
Debug::SymbolStorage::DecodeFrames(&ai.m_stackFrames[iFrame], numToDecode, lines);
|
||||
numStackLevels -= numToDecode;
|
||||
iFrame += numToDecode;
|
||||
const unsigned char decodeStep = 40;
|
||||
Debug::SymbolStorage::StackLine lines[decodeStep];
|
||||
unsigned char iFrame = 0;
|
||||
unsigned char numStackLevels = m_numStackLevels;
|
||||
while (numStackLevels > 0)
|
||||
{
|
||||
unsigned char numToDecode = AZStd::GetMin(decodeStep, numStackLevels);
|
||||
Debug::SymbolStorage::DecodeFrames(&ai.m_stackFrames[iFrame], numToDecode, lines);
|
||||
numStackLevels -= numToDecode;
|
||||
iFrame += numToDecode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AllocatorManager::Instance().DebugBreak(address, ai);
|
||||
|
||||
// statistics
|
||||
m_requestedBytes += byteSize;
|
||||
|
||||
size_t currentRequestedBytePeak;
|
||||
size_t newRequestedBytePeak;
|
||||
do
|
||||
{
|
||||
currentRequestedBytePeak = m_requestedBytesPeak.load(std::memory_order::memory_order_relaxed);
|
||||
newRequestedBytePeak = AZStd::GetMax(currentRequestedBytePeak, m_requestedBytes.load(std::memory_order::memory_order_relaxed));
|
||||
} while (!m_requestedBytesPeak.compare_exchange_weak(currentRequestedBytePeak, newRequestedBytePeak));
|
||||
|
||||
++m_requestedAllocs;
|
||||
|
||||
return &ai;
|
||||
}
|
||||
|
||||
AllocatorManager::Instance().DebugBreak(address, ai);
|
||||
|
||||
// statistics
|
||||
m_requestedBytes += byteSize;
|
||||
|
||||
size_t currentRequestedBytePeak;
|
||||
size_t newRequestedBytePeak;
|
||||
do
|
||||
//=========================================================================
|
||||
// UnregisterAllocation
|
||||
// [9/11/2009]
|
||||
//=========================================================================
|
||||
void AllocationRecords::UnregisterAllocation(void* address, size_t byteSize, size_t alignment, AllocationInfo* info)
|
||||
{
|
||||
currentRequestedBytePeak = m_requestedBytesPeak.load(std::memory_order::memory_order_relaxed);
|
||||
newRequestedBytePeak = AZStd::GetMax(currentRequestedBytePeak, m_requestedBytes.load(std::memory_order::memory_order_relaxed));
|
||||
} while (!m_requestedBytesPeak.compare_exchange_weak(currentRequestedBytePeak, newRequestedBytePeak));
|
||||
|
||||
++m_requestedAllocs;
|
||||
|
||||
return &ai;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// UnregisterAllocation
|
||||
// [9/11/2009]
|
||||
//=========================================================================
|
||||
void AllocationRecords::UnregisterAllocation(void* address, size_t byteSize, size_t alignment, AllocationInfo* info)
|
||||
{
|
||||
if (m_mode == RECORD_NO_RECORDS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (address == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AllocationInfo allocationInfo;
|
||||
{
|
||||
AZStd::scoped_lock lock(m_recordsMutex);
|
||||
Debug::AllocationRecordsType::iterator iter = m_records.find(address);
|
||||
// We cannot assert if an allocation does not exist because allocations may have been made before tracking was enabled.
|
||||
// It is currently impossible to actually track all allocations that happen before a certain point
|
||||
// AZ_Assert(iter!=m_records.end(), "Could not find address 0x%p in the allocator!", address);
|
||||
if (iter == m_records.end())
|
||||
if (m_mode == RECORD_NO_RECORDS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
allocationInfo = iter->second;
|
||||
m_records.erase(iter);
|
||||
|
||||
// try to be more aggressive and keep the memory footprint low.
|
||||
// \todo store the load factor at the last rehash to avoid unnecessary rehash
|
||||
if (m_records.load_factor() < 0.9f)
|
||||
if (address == nullptr)
|
||||
{
|
||||
m_records.rehash(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
AllocatorManager::Instance().DebugBreak(address, allocationInfo);
|
||||
|
||||
(void)byteSize;
|
||||
(void)alignment;
|
||||
AZ_Assert(byteSize==0||byteSize==allocationInfo.m_byteSize, "Mismatched byteSize at deallocation! You supplied an invalid value!");
|
||||
AZ_Assert(alignment==0||alignment==allocationInfo.m_alignment, "Mismatched alignment at deallocation! You supplied an invalid value!");
|
||||
|
||||
// statistics
|
||||
m_requestedBytes -= allocationInfo.m_byteSize;
|
||||
|
||||
#if defined(ENABLE_MEMORY_GUARD)
|
||||
// memory guard
|
||||
if (m_memoryGuardSize == sizeof(Debug::GuardValue))
|
||||
{
|
||||
if (m_isAutoIntegrityCheck)
|
||||
{
|
||||
// full integrity check
|
||||
IntegrityCheck();
|
||||
}
|
||||
else
|
||||
{
|
||||
// check current allocation
|
||||
char* guardAddress = reinterpret_cast<char*>(address)+allocationInfo.m_byteSize;
|
||||
Debug::GuardValue* guard = reinterpret_cast<Debug::GuardValue*>(guardAddress);
|
||||
if (!guard->Validate())
|
||||
{
|
||||
AZ_Printf("Memory", "Memory stomp located at address %p, part of allocation:", guardAddress);
|
||||
PrintAllocationsCB printAlloc(true);
|
||||
printAlloc(address, allocationInfo, m_numStackLevels);
|
||||
AZ_Assert(false, "MEMORY STOMP DETECTED!!!");
|
||||
}
|
||||
guard->~GuardValue();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// delete allocation record
|
||||
if (allocationInfo.m_namesBlock)
|
||||
{
|
||||
m_records.get_allocator().deallocate(allocationInfo.m_namesBlock, allocationInfo.m_namesBlockSize, 1);
|
||||
allocationInfo.m_namesBlock = nullptr;
|
||||
allocationInfo.m_namesBlockSize = 0;
|
||||
allocationInfo.m_name = nullptr;
|
||||
allocationInfo.m_fileName = nullptr;
|
||||
}
|
||||
if (allocationInfo.m_stackFrames)
|
||||
{
|
||||
m_records.get_allocator().deallocate(allocationInfo.m_stackFrames, sizeof(AZ::Debug::StackFrame)*m_numStackLevels, 1);
|
||||
allocationInfo.m_stackFrames = nullptr;
|
||||
}
|
||||
|
||||
if (info)
|
||||
{
|
||||
*info = allocationInfo;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// if requested set memory to a specific value.
|
||||
if (m_isMarkUnallocatedMemory)
|
||||
{
|
||||
memset(address, GetUnallocatedMarkValue(), byteSize);
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// ResizeAllocation
|
||||
// [9/20/2009]
|
||||
//=========================================================================
|
||||
void
|
||||
AllocationRecords::ResizeAllocation(void* address, size_t newSize)
|
||||
{
|
||||
if (m_mode == RECORD_NO_RECORDS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AllocationInfo* allocationInfo;
|
||||
{
|
||||
AZStd::scoped_lock lock(m_recordsMutex);
|
||||
Debug::AllocationRecordsType::iterator iter = m_records.find(address);
|
||||
AZ_Assert(iter != m_records.end(), "Could not find address 0x%p in the allocator!", address);
|
||||
allocationInfo = &iter->second;
|
||||
}
|
||||
AllocatorManager::Instance().DebugBreak(address, *allocationInfo);
|
||||
|
||||
#if defined(ENABLE_MEMORY_GUARD)
|
||||
if (m_memoryGuardSize == sizeof(Debug::GuardValue))
|
||||
{
|
||||
if (m_isAutoIntegrityCheck)
|
||||
{
|
||||
// full integrity check
|
||||
IntegrityCheck();
|
||||
}
|
||||
else
|
||||
{
|
||||
// check memory guard
|
||||
char* guardAddress = reinterpret_cast<char*>(address) + allocationInfo->m_byteSize;
|
||||
Debug::GuardValue* guard = reinterpret_cast<Debug::GuardValue*>(guardAddress);
|
||||
if (!guard->Validate())
|
||||
{
|
||||
AZ_Printf("Memory", "Memory stomp located at address %p, part of allocation:", guardAddress);
|
||||
PrintAllocationsCB printAlloc(true);
|
||||
printAlloc(address, iter->second, m_numStackLevels);
|
||||
AZ_Assert(false, "MEMORY STOMP DETECTED!!!");
|
||||
}
|
||||
guard->~GuardValue();
|
||||
}
|
||||
// init the new memory guard
|
||||
newSize -= sizeof(Debug::GuardValue);
|
||||
new(reinterpret_cast<char*>(address)+newSize) Debug::GuardValue();
|
||||
}
|
||||
#endif
|
||||
|
||||
// statistics
|
||||
m_requestedBytes -= allocationInfo->m_byteSize;
|
||||
m_requestedBytes += newSize;
|
||||
size_t currentRequestedBytePeak;
|
||||
size_t newRequestedBytePeak;
|
||||
do
|
||||
{
|
||||
currentRequestedBytePeak = m_requestedBytesPeak.load(std::memory_order::memory_order_relaxed);
|
||||
newRequestedBytePeak = AZStd::GetMax(currentRequestedBytePeak, m_requestedBytes.load(std::memory_order::memory_order_relaxed));
|
||||
} while (!m_requestedBytesPeak.compare_exchange_weak(currentRequestedBytePeak, newRequestedBytePeak));
|
||||
++m_requestedAllocs;
|
||||
|
||||
// update allocation size
|
||||
allocationInfo->m_byteSize = newSize;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// EnumerateAllocations
|
||||
// [9/29/2009]
|
||||
//=========================================================================
|
||||
void
|
||||
AllocationRecords::SetMode(Mode mode)
|
||||
{
|
||||
if (mode == RECORD_NO_RECORDS)
|
||||
{
|
||||
AllocationInfo allocationInfo;
|
||||
{
|
||||
AZStd::scoped_lock lock(m_recordsMutex);
|
||||
m_records.clear();
|
||||
Debug::AllocationRecordsType::iterator iter = m_records.find(address);
|
||||
// We cannot assert if an allocation does not exist because allocations may have been made before tracking was enabled.
|
||||
// It is currently impossible to actually track all allocations that happen before a certain point
|
||||
// AZ_Assert(iter!=m_records.end(), "Could not find address 0x%p in the allocator!", address);
|
||||
if (iter == m_records.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
allocationInfo = iter->second;
|
||||
m_records.erase(iter);
|
||||
|
||||
// try to be more aggressive and keep the memory footprint low.
|
||||
// \todo store the load factor at the last rehash to avoid unnecessary rehash
|
||||
if (m_records.load_factor() < 0.9f)
|
||||
{
|
||||
m_records.rehash(0);
|
||||
}
|
||||
}
|
||||
m_requestedBytes = 0;
|
||||
m_requestedBytesPeak = 0;
|
||||
m_requestedAllocs = 0;
|
||||
}
|
||||
|
||||
AZ_Warning("Memory", m_mode != RECORD_NO_RECORDS || mode == RECORD_NO_RECORDS, "Records recording was disabled and now it's enabled! You might get assert when you free memory, if a you have allocations which were not recorded!");
|
||||
AllocatorManager::Instance().DebugBreak(address, allocationInfo);
|
||||
|
||||
m_mode = mode;
|
||||
}
|
||||
(void)byteSize;
|
||||
(void)alignment;
|
||||
AZ_Assert(
|
||||
byteSize == 0 || byteSize == allocationInfo.m_byteSize, "Mismatched byteSize at deallocation! You supplied an invalid value!");
|
||||
AZ_Assert(
|
||||
alignment == 0 || alignment == allocationInfo.m_alignment,
|
||||
"Mismatched alignment at deallocation! You supplied an invalid value!");
|
||||
|
||||
//=========================================================================
|
||||
// EnumerateAllocations
|
||||
// [9/29/2009]
|
||||
//=========================================================================
|
||||
void
|
||||
AllocationRecords::EnumerateAllocations(AllocationInfoCBType cb)
|
||||
{
|
||||
// enumerate all allocations and stop if requested.
|
||||
// Since allocations can change during the iteration (code that prints out the records could allocate, which will
|
||||
// mutate m_records), we are going to make a copy and iterate the copy.
|
||||
Debug::AllocationRecordsType recordsCopy;
|
||||
{
|
||||
AZStd::scoped_lock lock(m_recordsMutex);
|
||||
recordsCopy = m_records;
|
||||
}
|
||||
for (Debug::AllocationRecordsType::const_iterator iter = recordsCopy.begin(); iter != recordsCopy.end(); ++iter)
|
||||
{
|
||||
if (!cb(iter->first, iter->second, m_numStackLevels))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// statistics
|
||||
m_requestedBytes -= allocationInfo.m_byteSize;
|
||||
|
||||
//=========================================================================
|
||||
// IntegrityCheck
|
||||
// [9/9/2011]
|
||||
//=========================================================================
|
||||
void
|
||||
AllocationRecords::IntegrityCheck() const
|
||||
{
|
||||
#if defined(ENABLE_MEMORY_GUARD)
|
||||
if (m_memoryGuardSize == sizeof(Debug::GuardValue))
|
||||
// memory guard
|
||||
if (m_memoryGuardSize == sizeof(Debug::GuardValue))
|
||||
{
|
||||
if (m_isAutoIntegrityCheck)
|
||||
{
|
||||
// full integrity check
|
||||
IntegrityCheck();
|
||||
}
|
||||
else
|
||||
{
|
||||
// check current allocation
|
||||
char* guardAddress = reinterpret_cast<char*>(address) + allocationInfo.m_byteSize;
|
||||
Debug::GuardValue* guard = reinterpret_cast<Debug::GuardValue*>(guardAddress);
|
||||
if (!guard->Validate())
|
||||
{
|
||||
AZ_Printf("Memory", "Memory stomp located at address %p, part of allocation:", guardAddress);
|
||||
PrintAllocationsCB printAlloc(true);
|
||||
printAlloc(address, allocationInfo, m_numStackLevels);
|
||||
AZ_Assert(false, "MEMORY STOMP DETECTED!!!");
|
||||
}
|
||||
guard->~GuardValue();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// delete allocation record
|
||||
if (allocationInfo.m_namesBlock)
|
||||
{
|
||||
m_records.get_allocator().deallocate(allocationInfo.m_namesBlock, allocationInfo.m_namesBlockSize, 1);
|
||||
allocationInfo.m_namesBlock = nullptr;
|
||||
allocationInfo.m_namesBlockSize = 0;
|
||||
allocationInfo.m_name = nullptr;
|
||||
allocationInfo.m_fileName = nullptr;
|
||||
}
|
||||
if (allocationInfo.m_stackFrames)
|
||||
{
|
||||
m_records.get_allocator().deallocate(allocationInfo.m_stackFrames, sizeof(AZ::Debug::StackFrame) * m_numStackLevels, 1);
|
||||
allocationInfo.m_stackFrames = nullptr;
|
||||
}
|
||||
|
||||
if (info)
|
||||
{
|
||||
*info = allocationInfo;
|
||||
}
|
||||
|
||||
// if requested set memory to a specific value.
|
||||
if (m_isMarkUnallocatedMemory)
|
||||
{
|
||||
memset(address, GetUnallocatedMarkValue(), byteSize);
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// ResizeAllocation
|
||||
// [9/20/2009]
|
||||
//=========================================================================
|
||||
void AllocationRecords::ResizeAllocation(void* address, size_t newSize)
|
||||
{
|
||||
if (m_mode == RECORD_NO_RECORDS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AllocationInfo* allocationInfo;
|
||||
{
|
||||
AZStd::scoped_lock lock(m_recordsMutex);
|
||||
Debug::AllocationRecordsType::iterator iter = m_records.find(address);
|
||||
AZ_Assert(iter != m_records.end(), "Could not find address 0x%p in the allocator!", address);
|
||||
allocationInfo = &iter->second;
|
||||
}
|
||||
AllocatorManager::Instance().DebugBreak(address, *allocationInfo);
|
||||
|
||||
#if defined(ENABLE_MEMORY_GUARD)
|
||||
if (m_memoryGuardSize == sizeof(Debug::GuardValue))
|
||||
{
|
||||
if (m_isAutoIntegrityCheck)
|
||||
{
|
||||
// full integrity check
|
||||
IntegrityCheck();
|
||||
}
|
||||
else
|
||||
{
|
||||
// check memory guard
|
||||
char* guardAddress = reinterpret_cast<char*>(address) + allocationInfo->m_byteSize;
|
||||
Debug::GuardValue* guard = reinterpret_cast<Debug::GuardValue*>(guardAddress);
|
||||
if (!guard->Validate())
|
||||
{
|
||||
AZ_Printf("Memory", "Memory stomp located at address %p, part of allocation:", guardAddress);
|
||||
PrintAllocationsCB printAlloc(true);
|
||||
printAlloc(address, iter->second, m_numStackLevels);
|
||||
AZ_Assert(false, "MEMORY STOMP DETECTED!!!");
|
||||
}
|
||||
guard->~GuardValue();
|
||||
}
|
||||
// init the new memory guard
|
||||
newSize -= sizeof(Debug::GuardValue);
|
||||
new (reinterpret_cast<char*>(address) + newSize) Debug::GuardValue();
|
||||
}
|
||||
#endif
|
||||
|
||||
// statistics
|
||||
m_requestedBytes -= allocationInfo->m_byteSize;
|
||||
m_requestedBytes += newSize;
|
||||
size_t currentRequestedBytePeak;
|
||||
size_t newRequestedBytePeak;
|
||||
do
|
||||
{
|
||||
currentRequestedBytePeak = m_requestedBytesPeak.load(std::memory_order::memory_order_relaxed);
|
||||
newRequestedBytePeak = AZStd::GetMax(currentRequestedBytePeak, m_requestedBytes.load(std::memory_order::memory_order_relaxed));
|
||||
} while (!m_requestedBytesPeak.compare_exchange_weak(currentRequestedBytePeak, newRequestedBytePeak));
|
||||
++m_requestedAllocs;
|
||||
|
||||
// update allocation size
|
||||
allocationInfo->m_byteSize = newSize;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// EnumerateAllocations
|
||||
// [9/29/2009]
|
||||
//=========================================================================
|
||||
void AllocationRecords::SetMode(Mode mode)
|
||||
{
|
||||
if (mode == RECORD_NO_RECORDS)
|
||||
{
|
||||
{
|
||||
AZStd::scoped_lock lock(m_recordsMutex);
|
||||
m_records.clear();
|
||||
}
|
||||
m_requestedBytes = 0;
|
||||
m_requestedBytesPeak = 0;
|
||||
m_requestedAllocs = 0;
|
||||
}
|
||||
|
||||
AZ_Warning(
|
||||
"Memory", m_mode != RECORD_NO_RECORDS || mode == RECORD_NO_RECORDS,
|
||||
"Records recording was disabled and now it's enabled! You might get assert when you free memory, if a you have allocations "
|
||||
"which were not recorded!");
|
||||
|
||||
m_mode = mode;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// EnumerateAllocations
|
||||
// [9/29/2009]
|
||||
//=========================================================================
|
||||
void AllocationRecords::EnumerateAllocations(AllocationInfoCBType cb)
|
||||
{
|
||||
// enumerate all allocations and stop if requested.
|
||||
// Since allocations can change during the iteration (code that prints out the records could allocate, which will
|
||||
// mutate m_records), we are going to make a copy and iterate the copy.
|
||||
Debug::AllocationRecordsType recordsCopy;
|
||||
{
|
||||
AZStd::scoped_lock lock(m_recordsMutex);
|
||||
@@ -455,67 +438,93 @@ AllocationRecords::IntegrityCheck() const
|
||||
}
|
||||
for (Debug::AllocationRecordsType::const_iterator iter = recordsCopy.begin(); iter != recordsCopy.end(); ++iter)
|
||||
{
|
||||
// check memory guard
|
||||
const char* guardAddress = reinterpret_cast<const char*>(iter->first)+ iter->second.m_byteSize;
|
||||
if (!reinterpret_cast<const Debug::GuardValue*>(guardAddress)->Validate())
|
||||
if (!cb(iter->first, iter->second, m_numStackLevels))
|
||||
{
|
||||
// We have to turn off the integrity check at this point if we want to succesfully report the memory
|
||||
// stomp we just found. If we don't turn this off, the printf just winds off the stack as each memory
|
||||
// allocation done therein recurses this same code.
|
||||
*const_cast<bool*>(&m_isAutoIntegrityCheck) = false;
|
||||
AZ_Printf("Memory", "Memory stomp located at address %p, part of allocation:", guardAddress);
|
||||
PrintAllocationsCB printAlloc(true);
|
||||
printAlloc(iter->first, iter->second, m_numStackLevels);
|
||||
AZ_Error("Memory", false, "MEMORY STOMP DETECTED!!!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// operator()
|
||||
// [9/29/2009]
|
||||
//=========================================================================
|
||||
bool
|
||||
PrintAllocationsCB::operator()(void* address, const AllocationInfo& info, unsigned char numStackLevels)
|
||||
{
|
||||
if (m_includeNameAndFilename && info.m_name)
|
||||
//=========================================================================
|
||||
// IntegrityCheck
|
||||
// [9/9/2011]
|
||||
//=========================================================================
|
||||
void AllocationRecords::IntegrityCheck() const
|
||||
{
|
||||
AZ_Printf("Memory", "Allocation Name: \"%s\" Addr: 0%p Size: %d Alignment: %d\n", info.m_name, address, info.m_byteSize, info.m_alignment);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Printf("Memory", "Allocation Addr: 0%p Size: %d Alignment: %d\n", address, info.m_byteSize, info.m_alignment);
|
||||
}
|
||||
|
||||
if (m_isDetailed)
|
||||
{
|
||||
if (!info.m_stackFrames)
|
||||
#if defined(ENABLE_MEMORY_GUARD)
|
||||
if (m_memoryGuardSize == sizeof(Debug::GuardValue))
|
||||
{
|
||||
AZ_Printf("Memory", " %s (%d)\n", info.m_fileName, info.m_lineNum);
|
||||
Debug::AllocationRecordsType recordsCopy;
|
||||
{
|
||||
AZStd::scoped_lock lock(m_recordsMutex);
|
||||
recordsCopy = m_records;
|
||||
}
|
||||
for (Debug::AllocationRecordsType::const_iterator iter = recordsCopy.begin(); iter != recordsCopy.end(); ++iter)
|
||||
{
|
||||
// check memory guard
|
||||
const char* guardAddress = reinterpret_cast<const char*>(iter->first) + iter->second.m_byteSize;
|
||||
if (!reinterpret_cast<const Debug::GuardValue*>(guardAddress)->Validate())
|
||||
{
|
||||
// We have to turn off the integrity check at this point if we want to succesfully report the memory
|
||||
// stomp we just found. If we don't turn this off, the printf just winds off the stack as each memory
|
||||
// allocation done therein recurses this same code.
|
||||
*const_cast<bool*>(&m_isAutoIntegrityCheck) = false;
|
||||
AZ_Printf("Memory", "Memory stomp located at address %p, part of allocation:", guardAddress);
|
||||
PrintAllocationsCB printAlloc(true);
|
||||
printAlloc(iter->first, iter->second, m_numStackLevels);
|
||||
AZ_Error("Memory", false, "MEMORY STOMP DETECTED!!!");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// operator()
|
||||
// [9/29/2009]
|
||||
//=========================================================================
|
||||
bool PrintAllocationsCB::operator()(void* address, const AllocationInfo& info, unsigned char numStackLevels)
|
||||
{
|
||||
if (m_includeNameAndFilename && info.m_name)
|
||||
{
|
||||
AZ_Printf(
|
||||
"Memory", "Allocation Name: \"%s\" Addr: 0%p Size: %d Alignment: %d\n", info.m_name, address, info.m_byteSize,
|
||||
info.m_alignment);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Allocation callstack
|
||||
const unsigned char decodeStep = 40;
|
||||
Debug::SymbolStorage::StackLine lines[decodeStep];
|
||||
unsigned char iFrame = 0;
|
||||
while (numStackLevels>0)
|
||||
AZ_Printf("Memory", "Allocation Addr: 0%p Size: %d Alignment: %d\n", address, info.m_byteSize, info.m_alignment);
|
||||
}
|
||||
|
||||
if (m_isDetailed)
|
||||
{
|
||||
if (!info.m_stackFrames)
|
||||
{
|
||||
unsigned char numToDecode = AZStd::GetMin(decodeStep, numStackLevels);
|
||||
Debug::SymbolStorage::DecodeFrames(&info.m_stackFrames[iFrame], numToDecode, lines);
|
||||
for (unsigned char i = 0; i < numToDecode; ++i)
|
||||
AZ_Printf("Memory", " %s (%d)\n", info.m_fileName, info.m_lineNum);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Allocation callstack
|
||||
const unsigned char decodeStep = 40;
|
||||
Debug::SymbolStorage::StackLine lines[decodeStep];
|
||||
unsigned char iFrame = 0;
|
||||
while (numStackLevels > 0)
|
||||
{
|
||||
if (info.m_stackFrames[iFrame+i].IsValid())
|
||||
unsigned char numToDecode = AZStd::GetMin(decodeStep, numStackLevels);
|
||||
Debug::SymbolStorage::DecodeFrames(&info.m_stackFrames[iFrame], numToDecode, lines);
|
||||
for (unsigned char i = 0; i < numToDecode; ++i)
|
||||
{
|
||||
AZ_Printf("Memory", " %s\n", lines[i]);
|
||||
if (info.m_stackFrames[iFrame + i].IsValid())
|
||||
{
|
||||
AZ_Printf("Memory", " %s\n", lines[i]);
|
||||
}
|
||||
}
|
||||
numStackLevels -= numToDecode;
|
||||
iFrame += numToDecode;
|
||||
}
|
||||
numStackLevels -= numToDecode;
|
||||
iFrame += numToDecode;
|
||||
}
|
||||
}
|
||||
return true; // continue enumerating
|
||||
}
|
||||
return true; // continue enumerating
|
||||
}
|
||||
|
||||
} // namespace AZ::Debug
|
||||
|
||||
@@ -9,191 +9,361 @@
|
||||
#include <AzCore/Memory/Memory.h>
|
||||
#include <AzCore/Memory/AllocatorManager.h>
|
||||
|
||||
using namespace AZ;
|
||||
#define RECORDING_ENABLED 0
|
||||
|
||||
AllocatorBase::AllocatorBase(IAllocatorAllocate* allocationSource, const char* name, const char* desc) :
|
||||
IAllocator(allocationSource),
|
||||
m_name(name),
|
||||
m_desc(desc)
|
||||
{
|
||||
}
|
||||
#if RECORDING_ENABLED
|
||||
|
||||
AllocatorBase::~AllocatorBase()
|
||||
{
|
||||
AZ_Assert(!m_isReady, "Allocator %s (%s) is being destructed without first having gone through proper calls to PreDestroy() and Destroy(). Use AllocatorInstance<> for global allocators or AllocatorWrapper<> for local allocators.", m_name, m_desc);
|
||||
}
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
#include <AzCore/std/parallel/mutex.h>
|
||||
#include <AzCore/std/parallel/scoped_lock.h>
|
||||
|
||||
const char* AllocatorBase::GetName() const
|
||||
namespace
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
const char* AllocatorBase::GetDescription() const
|
||||
{
|
||||
return m_desc;
|
||||
}
|
||||
|
||||
IAllocatorAllocate* AllocatorBase::GetSchema()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Debug::AllocationRecords* AllocatorBase::GetRecords()
|
||||
{
|
||||
return m_records;
|
||||
}
|
||||
|
||||
void AllocatorBase::SetRecords(Debug::AllocationRecords* records)
|
||||
{
|
||||
m_records = records;
|
||||
m_memoryGuardSize = records ? records->MemoryGuardSize() : 0;
|
||||
}
|
||||
|
||||
bool AllocatorBase::IsReady() const
|
||||
{
|
||||
return m_isReady;
|
||||
}
|
||||
|
||||
bool AllocatorBase::CanBeOverridden() const
|
||||
{
|
||||
return m_canBeOverridden;
|
||||
}
|
||||
|
||||
void AllocatorBase::PostCreate()
|
||||
{
|
||||
if (m_registrationEnabled)
|
||||
class DebugAllocator
|
||||
{
|
||||
if (AZ::Environment::IsReady())
|
||||
public:
|
||||
using pointer_type = void*;
|
||||
using size_type = AZStd::size_t;
|
||||
using difference_type = AZStd::ptrdiff_t;
|
||||
using allow_memory_leaks = AZStd::false_type; ///< Regular allocators should not leak.
|
||||
|
||||
AZ_FORCE_INLINE pointer_type allocate(size_t byteSize, size_t alignment, int = 0)
|
||||
{
|
||||
AllocatorManager::Instance().RegisterAllocator(this);
|
||||
return AZ_OS_MALLOC(byteSize, alignment);
|
||||
}
|
||||
AZ_FORCE_INLINE size_type resize(pointer_type, size_type)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
AZ_FORCE_INLINE void deallocate(pointer_type ptr, size_type, size_type)
|
||||
{
|
||||
AZ_OS_FREE(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct alignas(1) AllocatorOperation
|
||||
{
|
||||
enum OperationType : size_t
|
||||
{
|
||||
ALLOCATE,
|
||||
DEALLOCATE
|
||||
};
|
||||
OperationType m_type: 1;
|
||||
size_t m_size : 28; // Can represent up to 256Mb requests
|
||||
size_t m_alignment : 7; // Can represent up to 128 alignment
|
||||
size_t m_recordId : 28; // Can represent up to 256M simultaneous requests, we reuse ids
|
||||
};
|
||||
#pragma pack(pop)
|
||||
static_assert(sizeof(AllocatorOperation) == 8);
|
||||
|
||||
static AZStd::mutex s_operationsMutex = {};
|
||||
|
||||
static constexpr size_t s_maxNumberOfAllocationsToRecord = 16384;
|
||||
static size_t s_numberOfAllocationsRecorded = 0;
|
||||
static constexpr size_t s_allocationOperationCount = 5 * 1024;
|
||||
static AZStd::array<AllocatorOperation, s_allocationOperationCount> s_operations = {};
|
||||
static uint64_t s_operationCounter = 0;
|
||||
|
||||
static unsigned int s_nextRecordId = 1;
|
||||
using AllocatorOperationByAddress = AZStd::unordered_map<void*, AllocatorOperation, AZStd::less<void*>, DebugAllocator>;
|
||||
static AllocatorOperationByAddress s_allocatorOperationByAddress;
|
||||
using AvailableRecordIds = AZStd::vector<unsigned int, DebugAllocator>;
|
||||
AvailableRecordIds s_availableRecordIds;
|
||||
|
||||
void RecordAllocatorOperation(AllocatorOperation::OperationType type, void* ptr, size_t size = 0, size_t alignment = 0)
|
||||
{
|
||||
AZStd::scoped_lock<AZStd::mutex> lock(s_operationsMutex);
|
||||
if (s_operationCounter == s_allocationOperationCount)
|
||||
{
|
||||
AZ::IO::SystemFile file;
|
||||
int mode = AZ::IO::SystemFile::OpenMode::SF_OPEN_APPEND | AZ::IO::SystemFile::OpenMode::SF_OPEN_WRITE_ONLY;
|
||||
if (!file.Exists("memoryrecordings.bin"))
|
||||
{
|
||||
mode |= AZ::IO::SystemFile::OpenMode::SF_OPEN_CREATE;
|
||||
}
|
||||
file.Open("memoryrecordings.bin", mode);
|
||||
if (file.IsOpen())
|
||||
{
|
||||
file.Write(&s_operations, sizeof(AllocatorOperation) * s_allocationOperationCount);
|
||||
file.Close();
|
||||
}
|
||||
s_operationCounter = 0;
|
||||
}
|
||||
AllocatorOperation& operation = s_operations[s_operationCounter++];
|
||||
operation.m_type = type;
|
||||
if (type == AllocatorOperation::OperationType::ALLOCATE)
|
||||
{
|
||||
if (s_numberOfAllocationsRecorded > s_maxNumberOfAllocationsToRecord)
|
||||
{
|
||||
// reached limit of allocations, dont record anymore
|
||||
--s_operationCounter;
|
||||
return;
|
||||
}
|
||||
++s_numberOfAllocationsRecorded;
|
||||
operation.m_size = size;
|
||||
operation.m_alignment = alignment;
|
||||
unsigned int recordId = 0;
|
||||
if (!s_availableRecordIds.empty())
|
||||
{
|
||||
recordId = s_availableRecordIds.back();
|
||||
s_availableRecordIds.pop_back();
|
||||
}
|
||||
else
|
||||
{
|
||||
recordId = s_nextRecordId;
|
||||
++s_nextRecordId;
|
||||
}
|
||||
operation.m_recordId = recordId;
|
||||
auto it = s_allocatorOperationByAddress.emplace(ptr, operation);
|
||||
if (!it.second)
|
||||
{
|
||||
// double alloc or resize, leave the current record and return the id
|
||||
operation = it.first->second;
|
||||
s_availableRecordIds.emplace_back(recordId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AllocatorManager::PreRegisterAllocator(this);
|
||||
if (ptr == nullptr)
|
||||
{
|
||||
// common scenario, just record the operation
|
||||
operation.m_size = 0;
|
||||
operation.m_alignment = 0;
|
||||
operation.m_recordId = 0; // recordId = 0 will flag this case
|
||||
}
|
||||
else
|
||||
{
|
||||
auto it = s_allocatorOperationByAddress.find(ptr);
|
||||
if (it != s_allocatorOperationByAddress.end())
|
||||
{
|
||||
operation.m_size = it->second.m_size;
|
||||
operation.m_alignment = it->second.m_alignment;
|
||||
operation.m_recordId = it->second.m_recordId;
|
||||
s_availableRecordIds.push_back(it->second.m_recordId);
|
||||
s_allocatorOperationByAddress.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
// just dont record this operation
|
||||
--s_operationCounter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
const auto debugConfig = GetDebugConfig();
|
||||
if (!debugConfig.m_excludeFromDebugging)
|
||||
namespace AZ
|
||||
{
|
||||
AllocatorBase::AllocatorBase(IAllocatorAllocate* allocationSource, const char* name, const char* desc)
|
||||
: IAllocator(allocationSource)
|
||||
, m_name(name)
|
||||
, m_desc(desc)
|
||||
{
|
||||
SetRecords(aznew Debug::AllocationRecords((unsigned char)debugConfig.m_stackRecordLevels, debugConfig.m_usesMemoryGuards, debugConfig.m_marksUnallocatedMemory, GetName()));
|
||||
}
|
||||
|
||||
m_isReady = true;
|
||||
}
|
||||
|
||||
void AllocatorBase::PreDestroy()
|
||||
{
|
||||
Debug::AllocationRecords* allocatorRecords = GetRecords();
|
||||
if(allocatorRecords)
|
||||
AllocatorBase::~AllocatorBase()
|
||||
{
|
||||
delete allocatorRecords;
|
||||
SetRecords(nullptr);
|
||||
AZ_Assert(
|
||||
!m_isReady,
|
||||
"Allocator %s (%s) is being destructed without first having gone through proper calls to PreDestroy() and Destroy(). Use "
|
||||
"AllocatorInstance<> for global allocators or AllocatorWrapper<> for local allocators.",
|
||||
m_name, m_desc);
|
||||
}
|
||||
|
||||
if (m_registrationEnabled && AZ::AllocatorManager::IsReady())
|
||||
const char* AllocatorBase::GetName() const
|
||||
{
|
||||
AllocatorManager::Instance().UnRegisterAllocator(this);
|
||||
return m_name;
|
||||
}
|
||||
|
||||
m_isReady = false;
|
||||
}
|
||||
const char* AllocatorBase::GetDescription() const
|
||||
{
|
||||
return m_desc;
|
||||
}
|
||||
|
||||
void AllocatorBase::SetLazilyCreated(bool lazy)
|
||||
{
|
||||
m_isLazilyCreated = lazy;
|
||||
}
|
||||
IAllocatorAllocate* AllocatorBase::GetSchema()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool AllocatorBase::IsLazilyCreated() const
|
||||
{
|
||||
return m_isLazilyCreated;
|
||||
}
|
||||
Debug::AllocationRecords* AllocatorBase::GetRecords()
|
||||
{
|
||||
return m_records;
|
||||
}
|
||||
|
||||
void AllocatorBase::SetProfilingActive(bool active)
|
||||
{
|
||||
m_isProfilingActive = active;
|
||||
}
|
||||
void AllocatorBase::SetRecords(Debug::AllocationRecords* records)
|
||||
{
|
||||
m_records = records;
|
||||
m_memoryGuardSize = records ? records->MemoryGuardSize() : 0;
|
||||
}
|
||||
|
||||
bool AllocatorBase::IsProfilingActive() const
|
||||
{
|
||||
return m_isProfilingActive;
|
||||
}
|
||||
bool AllocatorBase::IsReady() const
|
||||
{
|
||||
return m_isReady;
|
||||
}
|
||||
|
||||
void AllocatorBase::DisableOverriding()
|
||||
{
|
||||
m_canBeOverridden = false;
|
||||
}
|
||||
bool AllocatorBase::CanBeOverridden() const
|
||||
{
|
||||
return m_canBeOverridden;
|
||||
}
|
||||
|
||||
void AllocatorBase::DisableRegistration()
|
||||
{
|
||||
m_registrationEnabled = false;
|
||||
}
|
||||
void AllocatorBase::PostCreate()
|
||||
{
|
||||
if (m_registrationEnabled)
|
||||
{
|
||||
if (AZ::Environment::IsReady())
|
||||
{
|
||||
AllocatorManager::Instance().RegisterAllocator(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
AllocatorManager::PreRegisterAllocator(this);
|
||||
}
|
||||
}
|
||||
|
||||
void AllocatorBase::ProfileAllocation(void* ptr, size_t byteSize, size_t alignment, const char* name, const char* fileName, int lineNum, int suppressStackRecord)
|
||||
{
|
||||
const auto debugConfig = GetDebugConfig();
|
||||
if (!debugConfig.m_excludeFromDebugging)
|
||||
{
|
||||
SetRecords(aznew Debug::AllocationRecords(
|
||||
(unsigned char)debugConfig.m_stackRecordLevels, debugConfig.m_usesMemoryGuards, debugConfig.m_marksUnallocatedMemory,
|
||||
GetName()));
|
||||
}
|
||||
|
||||
m_isReady = true;
|
||||
}
|
||||
|
||||
void AllocatorBase::PreDestroy()
|
||||
{
|
||||
Debug::AllocationRecords* allocatorRecords = GetRecords();
|
||||
if (allocatorRecords)
|
||||
{
|
||||
delete allocatorRecords;
|
||||
SetRecords(nullptr);
|
||||
}
|
||||
|
||||
if (m_registrationEnabled && AZ::AllocatorManager::IsReady())
|
||||
{
|
||||
AllocatorManager::Instance().UnRegisterAllocator(this);
|
||||
}
|
||||
|
||||
m_isReady = false;
|
||||
}
|
||||
|
||||
void AllocatorBase::SetLazilyCreated(bool lazy)
|
||||
{
|
||||
m_isLazilyCreated = lazy;
|
||||
}
|
||||
|
||||
bool AllocatorBase::IsLazilyCreated() const
|
||||
{
|
||||
return m_isLazilyCreated;
|
||||
}
|
||||
|
||||
void AllocatorBase::SetProfilingActive(bool active)
|
||||
{
|
||||
m_isProfilingActive = active;
|
||||
}
|
||||
|
||||
bool AllocatorBase::IsProfilingActive() const
|
||||
{
|
||||
return m_isProfilingActive;
|
||||
}
|
||||
|
||||
void AllocatorBase::DisableOverriding()
|
||||
{
|
||||
m_canBeOverridden = false;
|
||||
}
|
||||
|
||||
void AllocatorBase::DisableRegistration()
|
||||
{
|
||||
m_registrationEnabled = false;
|
||||
}
|
||||
|
||||
void AllocatorBase::ProfileAllocation(
|
||||
void* ptr, size_t byteSize, size_t alignment, const char* name, const char* fileName, int lineNum, int suppressStackRecord)
|
||||
{
|
||||
#if defined(AZ_HAS_VARIADIC_TEMPLATES) && defined(AZ_DEBUG_BUILD)
|
||||
++suppressStackRecord; // one more for the fact the ebus is a function
|
||||
++suppressStackRecord; // one more for the fact the ebus is a function
|
||||
#endif // AZ_HAS_VARIADIC_TEMPLATES
|
||||
|
||||
if (m_isProfilingActive)
|
||||
{
|
||||
auto records = GetRecords();
|
||||
if (records)
|
||||
if (m_isProfilingActive)
|
||||
{
|
||||
records->RegisterAllocation(ptr, byteSize, alignment, name, fileName, lineNum, suppressStackRecord + 1);
|
||||
auto records = GetRecords();
|
||||
if (records)
|
||||
{
|
||||
records->RegisterAllocation(ptr, byteSize, alignment, name, fileName, lineNum, suppressStackRecord + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AllocatorBase::ProfileDeallocation(void* ptr, size_t byteSize, size_t alignment, Debug::AllocationInfo* info)
|
||||
{
|
||||
if (m_isProfilingActive)
|
||||
#if RECORDING_ENABLED
|
||||
RecordAllocatorOperation(AllocatorOperation::ALLOCATE, ptr, byteSize, alignment);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AllocatorBase::ProfileDeallocation(void* ptr, size_t byteSize, size_t alignment, Debug::AllocationInfo* info)
|
||||
{
|
||||
auto records = GetRecords();
|
||||
if (records)
|
||||
if (m_isProfilingActive)
|
||||
{
|
||||
records->UnregisterAllocation(ptr, byteSize, alignment, info);
|
||||
auto records = GetRecords();
|
||||
if (records)
|
||||
{
|
||||
records->UnregisterAllocation(ptr, byteSize, alignment, info);
|
||||
}
|
||||
}
|
||||
#if RECORDING_ENABLED
|
||||
RecordAllocatorOperation(AllocatorOperation::DEALLOCATE, ptr, byteSize, alignment);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void AllocatorBase::ProfileReallocationBegin([[maybe_unused]] void* ptr, [[maybe_unused]] size_t newSize)
|
||||
{
|
||||
}
|
||||
|
||||
void AllocatorBase::ProfileReallocationEnd(void* ptr, void* newPtr, size_t newSize, size_t newAlignment)
|
||||
{
|
||||
if (m_isProfilingActive)
|
||||
void AllocatorBase::ProfileReallocationBegin([[maybe_unused]] void* ptr, [[maybe_unused]] size_t newSize)
|
||||
{
|
||||
Debug::AllocationInfo info;
|
||||
ProfileDeallocation(ptr, 0, 0, &info);
|
||||
ProfileAllocation(newPtr, newSize, newAlignment, info.m_name, info.m_fileName, info.m_lineNum, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void AllocatorBase::ProfileReallocation(void* ptr, void* newPtr, size_t newSize, size_t newAlignment)
|
||||
{
|
||||
ProfileReallocationEnd(ptr, newPtr, newSize, newAlignment);
|
||||
}
|
||||
|
||||
void AllocatorBase::ProfileResize(void* ptr, size_t newSize)
|
||||
{
|
||||
if (newSize && m_isProfilingActive)
|
||||
void AllocatorBase::ProfileReallocationEnd(void* ptr, void* newPtr, size_t newSize, size_t newAlignment)
|
||||
{
|
||||
auto records = GetRecords();
|
||||
if (records)
|
||||
if (m_isProfilingActive)
|
||||
{
|
||||
records->ResizeAllocation(ptr, newSize);
|
||||
Debug::AllocationInfo info;
|
||||
ProfileDeallocation(ptr, 0, 0, &info);
|
||||
ProfileAllocation(newPtr, newSize, newAlignment, info.m_name, info.m_fileName, info.m_lineNum, 0);
|
||||
}
|
||||
#if RECORDING_ENABLED
|
||||
RecordAllocatorOperation(AllocatorOperation::DEALLOCATE, ptr);
|
||||
RecordAllocatorOperation(AllocatorOperation::ALLOCATE, newPtr, newSize, newAlignment);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool AllocatorBase::OnOutOfMemory(size_t byteSize, size_t alignment, int flags, const char* name, const char* fileName, int lineNum)
|
||||
{
|
||||
if (AllocatorManager::IsReady() && AllocatorManager::Instance().m_outOfMemoryListener)
|
||||
void AllocatorBase::ProfileReallocation(void* ptr, void* newPtr, size_t newSize, size_t newAlignment)
|
||||
{
|
||||
AllocatorManager::Instance().m_outOfMemoryListener(this, byteSize, alignment, flags, name, fileName, lineNum);
|
||||
return true;
|
||||
ProfileReallocationEnd(ptr, newPtr, newSize, newAlignment);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AllocatorBase::ProfileResize(void* ptr, size_t newSize)
|
||||
{
|
||||
if (newSize && m_isProfilingActive)
|
||||
{
|
||||
auto records = GetRecords();
|
||||
if (records)
|
||||
{
|
||||
records->ResizeAllocation(ptr, newSize);
|
||||
}
|
||||
}
|
||||
#if RECORDING_ENABLED
|
||||
RecordAllocatorOperation(AllocatorOperation::ALLOCATE, ptr, newSize);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool AllocatorBase::OnOutOfMemory(size_t byteSize, size_t alignment, int flags, const char* name, const char* fileName, int lineNum)
|
||||
{
|
||||
if (AllocatorManager::IsReady() && AllocatorManager::Instance().m_outOfMemoryListener)
|
||||
{
|
||||
AllocatorManager::Instance().m_outOfMemoryListener(this, byteSize, alignment, flags, name, fileName, lineNum);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace AZ
|
||||
|
||||
@@ -13,186 +13,182 @@
|
||||
|
||||
#include <AzCore/std/functional.h>
|
||||
|
||||
using namespace AZ;
|
||||
|
||||
//=========================================================================
|
||||
// BestFitExternalMapAllocator
|
||||
// [1/28/2011]
|
||||
//=========================================================================
|
||||
BestFitExternalMapAllocator::BestFitExternalMapAllocator()
|
||||
: AllocatorBase(this, "BestFitExternalMapAllocator", "Best fit allocator with external tracking storage!")
|
||||
, m_schema(nullptr)
|
||||
{}
|
||||
|
||||
//=========================================================================
|
||||
// Create
|
||||
// [1/28/2011]
|
||||
//=========================================================================
|
||||
bool
|
||||
BestFitExternalMapAllocator::Create(const Descriptor& desc)
|
||||
namespace AZ
|
||||
{
|
||||
AZ_Assert(IsReady() == false, "BestFitExternalMapAllocator was already created!");
|
||||
if (IsReady())
|
||||
//=========================================================================
|
||||
// BestFitExternalMapAllocator
|
||||
// [1/28/2011]
|
||||
//=========================================================================
|
||||
BestFitExternalMapAllocator::BestFitExternalMapAllocator()
|
||||
: AllocatorBase(this, "BestFitExternalMapAllocator", "Best fit allocator with external tracking storage!")
|
||||
, m_schema(nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isReady = true;
|
||||
|
||||
m_desc = desc;
|
||||
BestFitExternalMapSchema::Descriptor schemaDesc;
|
||||
schemaDesc.m_mapAllocator = desc.m_mapAllocator;
|
||||
schemaDesc.m_memoryBlock = desc.m_memoryBlock;
|
||||
schemaDesc.m_memoryBlockByteSize = desc.m_memoryBlockByteSize;
|
||||
|
||||
m_schema = azcreate(BestFitExternalMapSchema, (schemaDesc), SystemAllocator);
|
||||
if (m_schema == nullptr)
|
||||
//=========================================================================
|
||||
// Create
|
||||
// [1/28/2011]
|
||||
//=========================================================================
|
||||
bool BestFitExternalMapAllocator::Create(const Descriptor& desc)
|
||||
{
|
||||
isReady = false;
|
||||
AZ_Assert(IsReady() == false, "BestFitExternalMapAllocator was already created!");
|
||||
if (IsReady())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isReady = true;
|
||||
|
||||
m_desc = desc;
|
||||
BestFitExternalMapSchema::Descriptor schemaDesc;
|
||||
schemaDesc.m_mapAllocator = desc.m_mapAllocator;
|
||||
schemaDesc.m_memoryBlock = desc.m_memoryBlock;
|
||||
schemaDesc.m_memoryBlockByteSize = desc.m_memoryBlockByteSize;
|
||||
|
||||
m_schema = azcreate(BestFitExternalMapSchema, (schemaDesc), SystemAllocator);
|
||||
if (m_schema == nullptr)
|
||||
{
|
||||
isReady = false;
|
||||
}
|
||||
|
||||
return isReady;
|
||||
}
|
||||
|
||||
return isReady;
|
||||
}
|
||||
//=========================================================================
|
||||
// Destroy
|
||||
// [1/28/2011]
|
||||
//=========================================================================
|
||||
void BestFitExternalMapAllocator::Destroy()
|
||||
{
|
||||
azdestroy(m_schema, SystemAllocator);
|
||||
m_schema = nullptr;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Destroy
|
||||
// [1/28/2011]
|
||||
//=========================================================================
|
||||
void
|
||||
BestFitExternalMapAllocator::Destroy()
|
||||
{
|
||||
azdestroy(m_schema, SystemAllocator);
|
||||
m_schema = nullptr;
|
||||
}
|
||||
AllocatorDebugConfig BestFitExternalMapAllocator::GetDebugConfig()
|
||||
{
|
||||
return AllocatorDebugConfig()
|
||||
.ExcludeFromDebugging(!m_desc.m_allocationRecords)
|
||||
.StackRecordLevels(m_desc.m_stackRecordLevels)
|
||||
.MarksUnallocatedMemory(false)
|
||||
.UsesMemoryGuards(false);
|
||||
}
|
||||
|
||||
AllocatorDebugConfig BestFitExternalMapAllocator::GetDebugConfig()
|
||||
{
|
||||
return AllocatorDebugConfig()
|
||||
.ExcludeFromDebugging(!m_desc.m_allocationRecords)
|
||||
.StackRecordLevels(m_desc.m_stackRecordLevels)
|
||||
.MarksUnallocatedMemory(false)
|
||||
.UsesMemoryGuards(false);
|
||||
}
|
||||
//=========================================================================
|
||||
// Allocate
|
||||
// [1/28/2011]
|
||||
//=========================================================================
|
||||
BestFitExternalMapAllocator::pointer_type BestFitExternalMapAllocator::Allocate(
|
||||
size_type byteSize,
|
||||
size_type alignment,
|
||||
int flags,
|
||||
[[maybe_unused]] const char* name,
|
||||
[[maybe_unused]] const char* fileName,
|
||||
[[maybe_unused]] int lineNum,
|
||||
unsigned int suppressStackRecord)
|
||||
{
|
||||
(void)suppressStackRecord;
|
||||
|
||||
//=========================================================================
|
||||
// Allocate
|
||||
// [1/28/2011]
|
||||
//=========================================================================
|
||||
BestFitExternalMapAllocator::pointer_type BestFitExternalMapAllocator::Allocate(
|
||||
size_type byteSize,
|
||||
size_type alignment,
|
||||
int flags,
|
||||
[[maybe_unused]] const char* name,
|
||||
[[maybe_unused]] const char* fileName,
|
||||
[[maybe_unused]] int lineNum,
|
||||
unsigned int suppressStackRecord)
|
||||
{
|
||||
(void)suppressStackRecord;
|
||||
AZ_Assert(byteSize > 0, "You can not allocate 0 bytes!");
|
||||
AZ_Assert((alignment & (alignment - 1)) == 0, "Alignment must be power of 2!");
|
||||
byteSize = MemorySizeAdjustedUp(byteSize);
|
||||
|
||||
AZ_Assert(byteSize > 0, "You can not allocate 0 bytes!");
|
||||
AZ_Assert((alignment & (alignment - 1)) == 0, "Alignment must be power of 2!");
|
||||
byteSize = MemorySizeAdjustedUp(byteSize);
|
||||
BestFitExternalMapAllocator::pointer_type address = m_schema->Allocate(byteSize, alignment, flags);
|
||||
AZ_Assert(
|
||||
address != nullptr, "BestFitExternalMapAllocator: Failed to allocate %d bytes aligned on %d (flags: 0x%08x) %s : %s (%d)!",
|
||||
byteSize, alignment, flags, name ? name : "(no name)", fileName ? fileName : "(no file name)", lineNum);
|
||||
AZ_MEMORY_PROFILE(ProfileAllocation(address, byteSize, alignment, name, fileName, lineNum, suppressStackRecord + 1));
|
||||
|
||||
BestFitExternalMapAllocator::pointer_type address = m_schema->Allocate(byteSize, alignment, flags);
|
||||
AZ_Assert(address != nullptr, "BestFitExternalMapAllocator: Failed to allocate %d bytes aligned on %d (flags: 0x%08x) %s : %s (%d)!", byteSize, alignment, flags, name ? name : "(no name)", fileName ? fileName : "(no file name)", lineNum);
|
||||
AZ_MEMORY_PROFILE(ProfileAllocation(address, byteSize, alignment, name, fileName, lineNum, suppressStackRecord + 1));
|
||||
return address;
|
||||
}
|
||||
|
||||
return address;
|
||||
}
|
||||
//=========================================================================
|
||||
// DeAllocate
|
||||
// [1/28/2011]
|
||||
//=========================================================================
|
||||
void BestFitExternalMapAllocator::DeAllocate(pointer_type ptr, size_type byteSize, size_type alignment)
|
||||
{
|
||||
byteSize = MemorySizeAdjustedUp(byteSize);
|
||||
AZ_MEMORY_PROFILE(ProfileDeallocation(ptr, byteSize, alignment, nullptr));
|
||||
|
||||
//=========================================================================
|
||||
// DeAllocate
|
||||
// [1/28/2011]
|
||||
//=========================================================================
|
||||
void
|
||||
BestFitExternalMapAllocator::DeAllocate(pointer_type ptr, size_type byteSize, size_type alignment)
|
||||
{
|
||||
byteSize = MemorySizeAdjustedUp(byteSize);
|
||||
AZ_MEMORY_PROFILE(ProfileDeallocation(ptr, byteSize, alignment, nullptr));
|
||||
(void)byteSize;
|
||||
(void)alignment;
|
||||
m_schema->DeAllocate(ptr);
|
||||
}
|
||||
|
||||
(void)byteSize;
|
||||
(void)alignment;
|
||||
m_schema->DeAllocate(ptr);
|
||||
}
|
||||
//=========================================================================
|
||||
// Resize
|
||||
// [1/28/2011]
|
||||
//=========================================================================
|
||||
BestFitExternalMapAllocator::size_type BestFitExternalMapAllocator::Resize(pointer_type ptr, size_type newSize)
|
||||
{
|
||||
(void)ptr;
|
||||
(void)newSize;
|
||||
/* todo */
|
||||
return 0;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Resize
|
||||
// [1/28/2011]
|
||||
//=========================================================================
|
||||
BestFitExternalMapAllocator::size_type
|
||||
BestFitExternalMapAllocator::Resize(pointer_type ptr, size_type newSize)
|
||||
{
|
||||
(void)ptr;
|
||||
(void)newSize;
|
||||
/* todo */
|
||||
return 0;
|
||||
}
|
||||
//=========================================================================
|
||||
// ReAllocate
|
||||
// [9/13/2011]
|
||||
//=========================================================================
|
||||
BestFitExternalMapAllocator::pointer_type BestFitExternalMapAllocator::ReAllocate(
|
||||
pointer_type ptr, size_type newSize, size_type newAlignment)
|
||||
{
|
||||
(void)ptr;
|
||||
(void)newSize;
|
||||
(void)newAlignment;
|
||||
AZ_Assert(false, "Not supported!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// ReAllocate
|
||||
// [9/13/2011]
|
||||
//=========================================================================
|
||||
BestFitExternalMapAllocator::pointer_type
|
||||
BestFitExternalMapAllocator::ReAllocate(pointer_type ptr, size_type newSize, size_type newAlignment)
|
||||
{
|
||||
(void)ptr;
|
||||
(void)newSize;
|
||||
(void)newAlignment;
|
||||
AZ_Assert(false, "Not supported!");
|
||||
return nullptr;
|
||||
}
|
||||
//=========================================================================
|
||||
// AllocationSize
|
||||
// [1/28/2011]
|
||||
//=========================================================================
|
||||
BestFitExternalMapAllocator::size_type BestFitExternalMapAllocator::AllocationSize(pointer_type ptr)
|
||||
{
|
||||
return MemorySizeAdjustedDown(m_schema->AllocationSize(ptr));
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// AllocationSize
|
||||
// [1/28/2011]
|
||||
//=========================================================================
|
||||
BestFitExternalMapAllocator::size_type
|
||||
BestFitExternalMapAllocator::AllocationSize(pointer_type ptr)
|
||||
{
|
||||
return MemorySizeAdjustedDown(m_schema->AllocationSize(ptr));
|
||||
}
|
||||
//=========================================================================
|
||||
// NumAllocatedBytes
|
||||
// [1/28/2011]
|
||||
//=========================================================================
|
||||
BestFitExternalMapAllocator::size_type BestFitExternalMapAllocator::NumAllocatedBytes() const
|
||||
{
|
||||
return m_schema->NumAllocatedBytes();
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// NumAllocatedBytes
|
||||
// [1/28/2011]
|
||||
//=========================================================================
|
||||
BestFitExternalMapAllocator::size_type
|
||||
BestFitExternalMapAllocator::NumAllocatedBytes() const
|
||||
{
|
||||
return m_schema->NumAllocatedBytes();
|
||||
}
|
||||
//=========================================================================
|
||||
// Capacity
|
||||
// [1/28/2011]
|
||||
//=========================================================================
|
||||
BestFitExternalMapAllocator::size_type BestFitExternalMapAllocator::Capacity() const
|
||||
{
|
||||
return m_schema->Capacity();
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Capacity
|
||||
// [1/28/2011]
|
||||
//=========================================================================
|
||||
BestFitExternalMapAllocator::size_type
|
||||
BestFitExternalMapAllocator::Capacity() const
|
||||
{
|
||||
return m_schema->Capacity();
|
||||
}
|
||||
//=========================================================================
|
||||
// GetMaxAllocationSize
|
||||
// [1/28/2011]
|
||||
//=========================================================================
|
||||
BestFitExternalMapAllocator::size_type BestFitExternalMapAllocator::GetMaxAllocationSize() const
|
||||
{
|
||||
return m_schema->GetMaxAllocationSize();
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// GetMaxAllocationSize
|
||||
// [1/28/2011]
|
||||
//=========================================================================
|
||||
BestFitExternalMapAllocator::size_type
|
||||
BestFitExternalMapAllocator::GetMaxAllocationSize() const
|
||||
{
|
||||
return m_schema->GetMaxAllocationSize();
|
||||
}
|
||||
auto BestFitExternalMapAllocator::GetMaxContiguousAllocationSize() const -> size_type
|
||||
{
|
||||
return m_schema->GetMaxContiguousAllocationSize();
|
||||
}
|
||||
|
||||
auto BestFitExternalMapAllocator::GetMaxContiguousAllocationSize() const -> size_type
|
||||
{
|
||||
return m_schema->GetMaxContiguousAllocationSize();
|
||||
}
|
||||
//=========================================================================
|
||||
// GetSubAllocator
|
||||
// [1/28/2011]
|
||||
//=========================================================================
|
||||
IAllocatorAllocate* BestFitExternalMapAllocator::GetSubAllocator()
|
||||
{
|
||||
return m_schema->GetSubAllocator();
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// GetSubAllocator
|
||||
// [1/28/2011]
|
||||
//=========================================================================
|
||||
IAllocatorAllocate*
|
||||
BestFitExternalMapAllocator::GetSubAllocator()
|
||||
{
|
||||
return m_schema->GetSubAllocator();
|
||||
}
|
||||
} // namespace AZ
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#ifndef AZ_BEST_FIT_EXT_MAP_ALLOCATOR_H
|
||||
#define AZ_BEST_FIT_EXT_MAP_ALLOCATOR_H
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Memory/Memory.h>
|
||||
|
||||
@@ -76,7 +75,3 @@ namespace AZ
|
||||
};
|
||||
}
|
||||
|
||||
#endif // AZ_BEST_FIT_EXT_MAP_ALLOCATOR_H
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
@@ -9,194 +9,199 @@
|
||||
#include <AzCore/Memory/BestFitExternalMapSchema.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
|
||||
using namespace AZ;
|
||||
|
||||
//=========================================================================
|
||||
// BestFitExternalMapSchema
|
||||
// [1/28/2011]
|
||||
//=========================================================================
|
||||
BestFitExternalMapSchema::BestFitExternalMapSchema(const Descriptor& desc)
|
||||
: m_desc(desc)
|
||||
, m_used(0)
|
||||
, m_freeChunksMap(FreeMapType::key_compare(), AZStdIAllocator(desc.m_mapAllocator != nullptr ? desc.m_mapAllocator : &AllocatorInstance<SystemAllocator>::Get()))
|
||||
, m_allocChunksMap(AllocMapType::hasher(), AllocMapType::key_eq(), AZStdIAllocator(desc.m_mapAllocator != nullptr ? desc.m_mapAllocator : &AllocatorInstance<SystemAllocator>::Get()))
|
||||
namespace AZ
|
||||
{
|
||||
if (m_desc.m_mapAllocator == nullptr)
|
||||
//=========================================================================
|
||||
// BestFitExternalMapSchema
|
||||
// [1/28/2011]
|
||||
//=========================================================================
|
||||
BestFitExternalMapSchema::BestFitExternalMapSchema(const Descriptor& desc)
|
||||
: m_desc(desc)
|
||||
, m_used(0)
|
||||
, m_freeChunksMap(
|
||||
FreeMapType::key_compare(),
|
||||
AZStdIAllocator(desc.m_mapAllocator != nullptr ? desc.m_mapAllocator : &AllocatorInstance<SystemAllocator>::Get()))
|
||||
, m_allocChunksMap(
|
||||
AllocMapType::hasher(),
|
||||
AllocMapType::key_eq(),
|
||||
AZStdIAllocator(desc.m_mapAllocator != nullptr ? desc.m_mapAllocator : &AllocatorInstance<SystemAllocator>::Get()))
|
||||
{
|
||||
m_desc.m_mapAllocator = &AllocatorInstance<SystemAllocator>::Get(); // used as our sub allocator
|
||||
}
|
||||
AZ_Assert(m_desc.m_memoryBlockByteSize > 0, "You must provide memory block size!");
|
||||
AZ_Assert(m_desc.m_memoryBlock != nullptr, "You must provide memory block allocated as you with!");
|
||||
//if( m_desc.m_memoryBlock == NULL) there is no point to automate this cause we need to flag this memory special, otherwise there is no point to use this allocator at all
|
||||
// m_desc.m_memoryBlock = azmalloc(SystemAllocator,m_desc.m_memoryBlockByteSize,16);
|
||||
m_freeChunksMap.insert(AZStd::make_pair(m_desc.m_memoryBlockByteSize, reinterpret_cast<char*>(m_desc.m_memoryBlock)));
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Allocate
|
||||
// [1/28/2011]
|
||||
//=========================================================================
|
||||
BestFitExternalMapSchema::pointer_type
|
||||
BestFitExternalMapSchema::Allocate(size_type byteSize, size_type alignment, int flags)
|
||||
{
|
||||
(void)flags;
|
||||
char* address = nullptr;
|
||||
AZ_Assert(alignment > 0 && (alignment & (alignment - 1)) == 0, "Alignment must be >0 and power of 2!");
|
||||
for (int i = 0; i < 2; ++i) // max 2 attempts to allocate
|
||||
{
|
||||
FreeMapType::iterator iter = m_freeChunksMap.find(byteSize);
|
||||
size_t blockSize = 0;
|
||||
char* blockAddress = nullptr;
|
||||
size_t preAllocBlockSize = 0;
|
||||
while (iter != m_freeChunksMap.end())
|
||||
if (m_desc.m_mapAllocator == nullptr)
|
||||
{
|
||||
blockSize = iter->first;
|
||||
blockAddress = iter->second;
|
||||
char* alignedAddr = PointerAlignUp(blockAddress, alignment);
|
||||
preAllocBlockSize = alignedAddr - blockAddress;
|
||||
if (preAllocBlockSize + byteSize <= blockSize)
|
||||
{
|
||||
m_freeChunksMap.erase(iter); // we have our allocation
|
||||
m_used += byteSize;
|
||||
address = alignedAddr;
|
||||
m_allocChunksMap.insert(AZStd::make_pair(address, byteSize));
|
||||
break;
|
||||
}
|
||||
++iter;
|
||||
m_desc.m_mapAllocator = &AllocatorInstance<SystemAllocator>::Get(); // used as our sub allocator
|
||||
}
|
||||
if (address != nullptr)
|
||||
AZ_Assert(m_desc.m_memoryBlockByteSize > 0, "You must provide memory block size!");
|
||||
AZ_Assert(m_desc.m_memoryBlock != nullptr, "You must provide memory block allocated as you with!");
|
||||
// if( m_desc.m_memoryBlock == NULL) there is no point to automate this cause we need to flag this memory special, otherwise there
|
||||
// is no point to use this allocator at all
|
||||
// m_desc.m_memoryBlock = azmalloc(SystemAllocator,m_desc.m_memoryBlockByteSize,16);
|
||||
m_freeChunksMap.insert(AZStd::make_pair(m_desc.m_memoryBlockByteSize, reinterpret_cast<char*>(m_desc.m_memoryBlock)));
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Allocate
|
||||
// [1/28/2011]
|
||||
//=========================================================================
|
||||
BestFitExternalMapSchema::pointer_type BestFitExternalMapSchema::Allocate(size_type byteSize, size_type alignment, int flags)
|
||||
{
|
||||
(void)flags;
|
||||
char* address = nullptr;
|
||||
AZ_Assert(alignment > 0 && (alignment & (alignment - 1)) == 0, "Alignment must be >0 and power of 2!");
|
||||
for (int i = 0; i < 2; ++i) // max 2 attempts to allocate
|
||||
{
|
||||
// split blocks
|
||||
if (preAllocBlockSize) // if we have a block before the alignment
|
||||
FreeMapType::iterator iter = m_freeChunksMap.find(byteSize);
|
||||
size_t blockSize = 0;
|
||||
char* blockAddress = nullptr;
|
||||
size_t preAllocBlockSize = 0;
|
||||
while (iter != m_freeChunksMap.end())
|
||||
{
|
||||
m_freeChunksMap.insert(AZStd::make_pair(preAllocBlockSize, blockAddress));
|
||||
}
|
||||
size_t postAllocBlockSize = blockSize - preAllocBlockSize - byteSize;
|
||||
if (postAllocBlockSize)
|
||||
{
|
||||
m_freeChunksMap.insert(AZStd::make_pair(postAllocBlockSize, address + byteSize));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
GarbageCollect();
|
||||
}
|
||||
}
|
||||
return address;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// DeAllocate
|
||||
// [1/28/2011]
|
||||
//=========================================================================
|
||||
void
|
||||
BestFitExternalMapSchema::DeAllocate(pointer_type ptr)
|
||||
{
|
||||
if (ptr == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
AllocMapType::iterator iter = m_allocChunksMap.find(reinterpret_cast<char*>(ptr));
|
||||
if (iter != m_allocChunksMap.end())
|
||||
{
|
||||
m_used -= iter->second;
|
||||
m_freeChunksMap.insert(AZStd::make_pair(iter->second, iter->first));
|
||||
m_allocChunksMap.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// AllocationSize
|
||||
// [1/28/2011]
|
||||
//=========================================================================
|
||||
BestFitExternalMapSchema::size_type
|
||||
BestFitExternalMapSchema::AllocationSize(pointer_type ptr)
|
||||
{
|
||||
AllocMapType::iterator iter = m_allocChunksMap.find(reinterpret_cast<char*>(ptr));
|
||||
if (iter != m_allocChunksMap.end())
|
||||
{
|
||||
return iter->second;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// GetMaxAllocationSize
|
||||
// [1/28/2011]
|
||||
//=========================================================================
|
||||
BestFitExternalMapSchema::size_type
|
||||
BestFitExternalMapSchema::GetMaxAllocationSize() const
|
||||
{
|
||||
if (!m_freeChunksMap.empty())
|
||||
{
|
||||
return m_freeChunksMap.rbegin()->first;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto BestFitExternalMapSchema::GetMaxContiguousAllocationSize() const -> size_type
|
||||
{
|
||||
// Return the maximum size of any single allocation
|
||||
return AZ_CORE_MAX_ALLOCATOR_SIZE;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// GarbageCollect
|
||||
// [1/28/2011]
|
||||
//=========================================================================
|
||||
void
|
||||
BestFitExternalMapSchema::GarbageCollect()
|
||||
{
|
||||
for (FreeMapType::iterator curBlock = m_freeChunksMap.begin(); curBlock != m_freeChunksMap.end(); )
|
||||
{
|
||||
char* curStart = curBlock->second;
|
||||
char* curEnd = curStart + curBlock->first;
|
||||
bool isMerge = false;
|
||||
for (FreeMapType::iterator nextBlock = curBlock++; nextBlock != m_freeChunksMap.end(); )
|
||||
{
|
||||
char* nextStart = nextBlock->second;
|
||||
char* nextEnd = nextStart + nextBlock->first;
|
||||
if (curStart == nextEnd)
|
||||
{
|
||||
// merge
|
||||
size_t newBlockSize = curBlock->first + nextBlock->first;
|
||||
char* newBlockAddress = nextStart;
|
||||
m_freeChunksMap.erase(nextBlock);
|
||||
FreeMapType::iterator toErase = curBlock;
|
||||
++curBlock;
|
||||
m_freeChunksMap.erase(toErase);
|
||||
FreeMapType::iterator newBlock = m_freeChunksMap.insert(AZStd::make_pair(newBlockSize, newBlockAddress)).first;
|
||||
if (curBlock != m_freeChunksMap.end() && newBlockSize < curBlock->first) // if the newBlock in before the next in the list, update next in the list to current
|
||||
blockSize = iter->first;
|
||||
blockAddress = iter->second;
|
||||
char* alignedAddr = PointerAlignUp(blockAddress, alignment);
|
||||
preAllocBlockSize = alignedAddr - blockAddress;
|
||||
if (preAllocBlockSize + byteSize <= blockSize)
|
||||
{
|
||||
curBlock = newBlock;
|
||||
m_freeChunksMap.erase(iter); // we have our allocation
|
||||
m_used += byteSize;
|
||||
address = alignedAddr;
|
||||
m_allocChunksMap.insert(AZStd::make_pair(address, byteSize));
|
||||
break;
|
||||
}
|
||||
isMerge = true;
|
||||
break;
|
||||
++iter;
|
||||
}
|
||||
else if (curEnd == nextStart)
|
||||
if (address != nullptr)
|
||||
{
|
||||
// merge
|
||||
size_t newBlockSize = curBlock->first + nextBlock->first;
|
||||
char* newBlockAddress = curStart;
|
||||
m_freeChunksMap.erase(nextBlock);
|
||||
FreeMapType::iterator toErase = curBlock;
|
||||
++curBlock;
|
||||
m_freeChunksMap.erase(toErase);
|
||||
FreeMapType::iterator newBlock = m_freeChunksMap.insert(AZStd::make_pair(newBlockSize, newBlockAddress)).first;
|
||||
if (curBlock != m_freeChunksMap.end() && newBlockSize < curBlock->first) // if the newBlock in before the next in the list, update next in the list to current
|
||||
// split blocks
|
||||
if (preAllocBlockSize) // if we have a block before the alignment
|
||||
{
|
||||
curBlock = newBlock;
|
||||
m_freeChunksMap.insert(AZStd::make_pair(preAllocBlockSize, blockAddress));
|
||||
}
|
||||
isMerge = true;
|
||||
size_t postAllocBlockSize = blockSize - preAllocBlockSize - byteSize;
|
||||
if (postAllocBlockSize)
|
||||
{
|
||||
m_freeChunksMap.insert(AZStd::make_pair(postAllocBlockSize, address + byteSize));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
++nextBlock;
|
||||
else
|
||||
{
|
||||
GarbageCollect();
|
||||
}
|
||||
}
|
||||
if (!isMerge)
|
||||
return address;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// DeAllocate
|
||||
// [1/28/2011]
|
||||
//=========================================================================
|
||||
void BestFitExternalMapSchema::DeAllocate(pointer_type ptr)
|
||||
{
|
||||
if (ptr == nullptr)
|
||||
{
|
||||
++curBlock;
|
||||
return;
|
||||
}
|
||||
AllocMapType::iterator iter = m_allocChunksMap.find(reinterpret_cast<char*>(ptr));
|
||||
if (iter != m_allocChunksMap.end())
|
||||
{
|
||||
m_used -= iter->second;
|
||||
m_freeChunksMap.insert(AZStd::make_pair(iter->second, iter->first));
|
||||
m_allocChunksMap.erase(iter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// AllocationSize
|
||||
// [1/28/2011]
|
||||
//=========================================================================
|
||||
BestFitExternalMapSchema::size_type BestFitExternalMapSchema::AllocationSize(pointer_type ptr)
|
||||
{
|
||||
AllocMapType::iterator iter = m_allocChunksMap.find(reinterpret_cast<char*>(ptr));
|
||||
if (iter != m_allocChunksMap.end())
|
||||
{
|
||||
return iter->second;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// GetMaxAllocationSize
|
||||
// [1/28/2011]
|
||||
//=========================================================================
|
||||
BestFitExternalMapSchema::size_type BestFitExternalMapSchema::GetMaxAllocationSize() const
|
||||
{
|
||||
if (!m_freeChunksMap.empty())
|
||||
{
|
||||
return m_freeChunksMap.rbegin()->first;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto BestFitExternalMapSchema::GetMaxContiguousAllocationSize() const -> size_type
|
||||
{
|
||||
// Return the maximum size of any single allocation
|
||||
return AZ_CORE_MAX_ALLOCATOR_SIZE;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// GarbageCollect
|
||||
// [1/28/2011]
|
||||
//=========================================================================
|
||||
void BestFitExternalMapSchema::GarbageCollect()
|
||||
{
|
||||
for (FreeMapType::iterator curBlock = m_freeChunksMap.begin(); curBlock != m_freeChunksMap.end();)
|
||||
{
|
||||
char* curStart = curBlock->second;
|
||||
char* curEnd = curStart + curBlock->first;
|
||||
bool isMerge = false;
|
||||
for (FreeMapType::iterator nextBlock = curBlock++; nextBlock != m_freeChunksMap.end();)
|
||||
{
|
||||
char* nextStart = nextBlock->second;
|
||||
char* nextEnd = nextStart + nextBlock->first;
|
||||
if (curStart == nextEnd)
|
||||
{
|
||||
// merge
|
||||
size_t newBlockSize = curBlock->first + nextBlock->first;
|
||||
char* newBlockAddress = nextStart;
|
||||
m_freeChunksMap.erase(nextBlock);
|
||||
FreeMapType::iterator toErase = curBlock;
|
||||
++curBlock;
|
||||
m_freeChunksMap.erase(toErase);
|
||||
FreeMapType::iterator newBlock = m_freeChunksMap.insert(AZStd::make_pair(newBlockSize, newBlockAddress)).first;
|
||||
// if the newBlock in before the next in the list, update next in the list to current
|
||||
if (curBlock != m_freeChunksMap.end() && newBlockSize < curBlock->first)
|
||||
{
|
||||
curBlock = newBlock;
|
||||
}
|
||||
isMerge = true;
|
||||
break;
|
||||
}
|
||||
else if (curEnd == nextStart)
|
||||
{
|
||||
// merge
|
||||
size_t newBlockSize = curBlock->first + nextBlock->first;
|
||||
char* newBlockAddress = curStart;
|
||||
m_freeChunksMap.erase(nextBlock);
|
||||
FreeMapType::iterator toErase = curBlock;
|
||||
++curBlock;
|
||||
m_freeChunksMap.erase(toErase);
|
||||
FreeMapType::iterator newBlock = m_freeChunksMap.insert(AZStd::make_pair(newBlockSize, newBlockAddress)).first;
|
||||
// if the newBlock in before the next in the list, update next in the list to current
|
||||
if (curBlock != m_freeChunksMap.end() && newBlockSize < curBlock->first)
|
||||
{
|
||||
curBlock = newBlock;
|
||||
}
|
||||
isMerge = true;
|
||||
break;
|
||||
}
|
||||
++nextBlock;
|
||||
}
|
||||
if (!isMerge)
|
||||
{
|
||||
++curBlock;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AZ
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#ifndef AZ_BEST_FIT_EXT_MAP_ALLOCATION_SCHEME_H
|
||||
#define AZ_BEST_FIT_EXT_MAP_ALLOCATION_SCHEME_H
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/Memory/Memory.h>
|
||||
@@ -77,8 +76,3 @@ namespace AZ
|
||||
AllocMapType m_allocChunksMap;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // AZ_BEST_FIT_EXT_MAP_ALLOCATION_SCHEME_H
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
@@ -115,6 +115,7 @@ namespace AZ
|
||||
m_ownMemoryBlock[i] = false;
|
||||
}
|
||||
|
||||
AZ_Assert(m_desc.m_numMemoryBlocks > 0, "At least one memory block is required");
|
||||
for (int i = 0; i < m_desc.m_numMemoryBlocks; ++i)
|
||||
{
|
||||
if (m_desc.m_memoryBlocks[i] == nullptr) // Allocate memory block if requested!
|
||||
@@ -131,17 +132,6 @@ namespace AZ
|
||||
|
||||
m_capacity += m_desc.m_memoryBlocksByteSize[i];
|
||||
}
|
||||
|
||||
if (m_desc.m_numMemoryBlocks == 0)
|
||||
{
|
||||
// Create default memory space if we can to serve for default allocations
|
||||
m_memSpaces[0] = AZDLMalloc::create_mspace(0, m_desc.m_isMultithreadAlloc);
|
||||
if (m_memSpaces[0])
|
||||
{
|
||||
AZDLMalloc::mspace_az_set_expandable(m_memSpaces[0], true);
|
||||
m_capacity = Platform::GetHeapCapacity();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HeapSchema::~HeapSchema()
|
||||
|
||||
@@ -32,17 +32,11 @@ namespace AZ
|
||||
*/
|
||||
struct Descriptor
|
||||
{
|
||||
Descriptor()
|
||||
: m_numMemoryBlocks(0)
|
||||
, m_isMultithreadAlloc(true)
|
||||
{}
|
||||
|
||||
static const int m_memoryBlockAlignment = 64 * 1024;
|
||||
static const int m_maxNumBlocks = 5;
|
||||
int m_numMemoryBlocks; ///< Number of memory blocks to use.
|
||||
void* m_memoryBlocks[m_maxNumBlocks]; ///< Pointers to provided memory blocks or NULL if you want the system to allocate them for you with the System Allocator.
|
||||
size_t m_memoryBlocksByteSize[m_maxNumBlocks]; ///< Sizes of different memory blocks, if m_memoryBlock is 0 the block will be allocated for you with the System Allocator.
|
||||
bool m_isMultithreadAlloc; ///< Set to true to enable multi threading safe allocation.
|
||||
int m_numMemoryBlocks = 1; ///< Number of memory blocks to use.
|
||||
void* m_memoryBlocks[m_maxNumBlocks] = {}; ///< Pointers to provided memory blocks or NULL if you want the system to allocate them for you with the System Allocator.
|
||||
size_t m_memoryBlocksByteSize[m_maxNumBlocks] = {4 * 1024}; ///< Sizes of different memory blocks, if m_memoryBlock is 0 the block will be allocated for you with the System Allocator.
|
||||
bool m_isMultithreadAlloc = true; ///< Set to true to enable multi threading safe allocation.
|
||||
};
|
||||
|
||||
HeapSchema(const Descriptor& desc);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,8 +5,7 @@
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#ifndef AZ_POOL_ALLOCATION_SCHEME_H
|
||||
#define AZ_POOL_ALLOCATION_SCHEME_H
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
|
||||
@@ -164,8 +163,3 @@ namespace AZ
|
||||
template<class Allocator>
|
||||
AZ_THREAD_LOCAL ThreadPoolData* ThreadPoolSchemaHelper<Allocator>::m_threadData = 0;
|
||||
}
|
||||
|
||||
#endif // AZ_POOL_ALLOCATION_SCHEME_H
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
@@ -18,299 +18,289 @@
|
||||
|
||||
#define AZCORE_SYSTEM_ALLOCATOR_HPHA 1
|
||||
#define AZCORE_SYSTEM_ALLOCATOR_MALLOC 2
|
||||
#define AZCORE_SYSTEM_ALLOCATOR_HEAP 3
|
||||
|
||||
#if !defined(AZCORE_SYSTEM_ALLOCATOR)
|
||||
// define the default
|
||||
#define AZCORE_SYSTEM_ALLOCATOR AZCORE_SYSTEM_ALLOCATOR_HPHA
|
||||
// define the default
|
||||
#define AZCORE_SYSTEM_ALLOCATOR AZCORE_SYSTEM_ALLOCATOR_HPHA
|
||||
#endif
|
||||
|
||||
#if AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HPHA
|
||||
#include <AzCore/Memory/HphaSchema.h>
|
||||
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_MALLOC
|
||||
#include <AzCore/Memory/MallocSchema.h>
|
||||
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HEAP
|
||||
#include <AzCore/Memory/HeapSchema.h>
|
||||
#else
|
||||
#error "Invalid allocator selected for SystemAllocator"
|
||||
#endif
|
||||
|
||||
|
||||
using namespace AZ;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Globals - we use global storage for the first memory schema, since we can't use dynamic memory!
|
||||
static bool g_isSystemSchemaUsed = false;
|
||||
namespace AZ
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Globals - we use global storage for the first memory schema, since we can't use dynamic memory!
|
||||
static bool g_isSystemSchemaUsed = false;
|
||||
#if AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HPHA
|
||||
static AZStd::aligned_storage<sizeof(HphaSchema), AZStd::alignment_of<HphaSchema>::value>::type g_systemSchema;
|
||||
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_MALLOC
|
||||
static AZStd::aligned_storage<sizeof(MallocSchema), AZStd::alignment_of<MallocSchema>::value>::type g_systemSchema;
|
||||
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HEAP
|
||||
static AZStd::aligned_storage<sizeof(HeapSchema), AZStd::alignment_of<HeapSchema>::value>::type g_systemSchema;
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//=========================================================================
|
||||
// SystemAllocator
|
||||
// [9/2/2009]
|
||||
//=========================================================================
|
||||
SystemAllocator::SystemAllocator()
|
||||
: AllocatorBase(this, "SystemAllocator", "Fundamental generic memory allocator")
|
||||
, m_isCustom(false)
|
||||
, m_allocator(nullptr)
|
||||
, m_ownsOSAllocator(false)
|
||||
{
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// ~SystemAllocator
|
||||
//=========================================================================
|
||||
SystemAllocator::~SystemAllocator()
|
||||
{
|
||||
if (IsReady())
|
||||
//=========================================================================
|
||||
// SystemAllocator
|
||||
// [9/2/2009]
|
||||
//=========================================================================
|
||||
SystemAllocator::SystemAllocator()
|
||||
: AllocatorBase(this, "SystemAllocator", "Fundamental generic memory allocator")
|
||||
, m_isCustom(false)
|
||||
, m_allocator(nullptr)
|
||||
, m_ownsOSAllocator(false)
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// ~Create
|
||||
// [9/2/2009]
|
||||
//=========================================================================
|
||||
bool
|
||||
SystemAllocator::Create(const Descriptor& desc)
|
||||
{
|
||||
AZ_Assert(IsReady() == false, "System allocator was already created!");
|
||||
if (IsReady())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_desc = desc;
|
||||
|
||||
if (!AllocatorInstance<OSAllocator>::IsReady())
|
||||
//=========================================================================
|
||||
// ~SystemAllocator
|
||||
//=========================================================================
|
||||
SystemAllocator::~SystemAllocator()
|
||||
{
|
||||
m_ownsOSAllocator = true;
|
||||
AllocatorInstance<OSAllocator>::Create();
|
||||
}
|
||||
bool isReady = false;
|
||||
if (desc.m_custom)
|
||||
{
|
||||
m_isCustom = true;
|
||||
m_allocator = desc.m_custom;
|
||||
isReady = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_isCustom = false;
|
||||
#if AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HPHA
|
||||
HphaSchema::Descriptor heapDesc;
|
||||
heapDesc.m_pageSize = desc.m_heap.m_pageSize;
|
||||
heapDesc.m_poolPageSize = desc.m_heap.m_poolPageSize;
|
||||
AZ_Assert(desc.m_heap.m_numFixedMemoryBlocks <= 1, "We support max1 memory block at the moment!");
|
||||
if (desc.m_heap.m_numFixedMemoryBlocks > 0)
|
||||
if (IsReady())
|
||||
{
|
||||
heapDesc.m_fixedMemoryBlock = desc.m_heap.m_fixedMemoryBlocks[0];
|
||||
heapDesc.m_fixedMemoryBlockByteSize = desc.m_heap.m_fixedMemoryBlocksByteSize[0];
|
||||
Destroy();
|
||||
}
|
||||
heapDesc.m_subAllocator = desc.m_heap.m_subAllocator;
|
||||
heapDesc.m_isPoolAllocations = desc.m_heap.m_isPoolAllocations;
|
||||
// Fix SystemAllocator from growing in small chunks
|
||||
heapDesc.m_systemChunkSize = desc.m_heap.m_systemChunkSize;
|
||||
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_MALLOC
|
||||
MallocSchema::Descriptor heapDesc;
|
||||
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HEAP
|
||||
HeapSchema::Descriptor heapDesc;
|
||||
memcpy(heapDesc.m_memoryBlocks, desc.m_heap.m_memoryBlocks, sizeof(heapDesc.m_memoryBlocks));
|
||||
memcpy(heapDesc.m_memoryBlocksByteSize, desc.m_heap.m_memoryBlocksByteSize, sizeof(heapDesc.m_memoryBlocksByteSize));
|
||||
heapDesc.m_numMemoryBlocks = desc.m_heap.m_numMemoryBlocks;
|
||||
#endif
|
||||
if (&AllocatorInstance<SystemAllocator>::Get() == this) // if we are the system allocator
|
||||
{
|
||||
AZ_Assert(!g_isSystemSchemaUsed, "AZ::SystemAllocator MUST be created first! It's the source of all allocations!");
|
||||
}
|
||||
|
||||
#if AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HPHA
|
||||
m_allocator = new(&g_systemSchema)HphaSchema(heapDesc);
|
||||
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_MALLOC
|
||||
m_allocator = new(&g_systemSchema)MallocSchema(heapDesc);
|
||||
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HEAP
|
||||
m_allocator = new(&g_systemSchema)HeapSchema(heapDesc);
|
||||
#endif
|
||||
g_isSystemSchemaUsed = true;
|
||||
//=========================================================================
|
||||
// ~Create
|
||||
// [9/2/2009]
|
||||
//=========================================================================
|
||||
bool SystemAllocator::Create(const Descriptor& desc)
|
||||
{
|
||||
AZ_Assert(IsReady() == false, "System allocator was already created!");
|
||||
if (IsReady())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_desc = desc;
|
||||
|
||||
if (!AllocatorInstance<OSAllocator>::IsReady())
|
||||
{
|
||||
m_ownsOSAllocator = true;
|
||||
AllocatorInstance<OSAllocator>::Create();
|
||||
}
|
||||
bool isReady = false;
|
||||
if (desc.m_custom)
|
||||
{
|
||||
m_isCustom = true;
|
||||
m_allocator = desc.m_custom;
|
||||
isReady = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// this class should be inheriting from SystemAllocator
|
||||
AZ_Assert(AllocatorInstance<SystemAllocator>::IsReady(), "System allocator must be created before any other allocator! They allocate from it.");
|
||||
m_isCustom = false;
|
||||
#if AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HPHA
|
||||
HphaSchema::Descriptor heapDesc;
|
||||
heapDesc.m_pageSize = desc.m_heap.m_pageSize;
|
||||
heapDesc.m_poolPageSize = desc.m_heap.m_poolPageSize;
|
||||
AZ_Assert(desc.m_heap.m_numFixedMemoryBlocks <= 1, "We support max1 memory block at the moment!");
|
||||
if (desc.m_heap.m_numFixedMemoryBlocks > 0)
|
||||
{
|
||||
heapDesc.m_fixedMemoryBlock = desc.m_heap.m_fixedMemoryBlocks[0];
|
||||
heapDesc.m_fixedMemoryBlockByteSize = desc.m_heap.m_fixedMemoryBlocksByteSize[0];
|
||||
}
|
||||
heapDesc.m_subAllocator = desc.m_heap.m_subAllocator;
|
||||
heapDesc.m_isPoolAllocations = desc.m_heap.m_isPoolAllocations;
|
||||
// Fix SystemAllocator from growing in small chunks
|
||||
heapDesc.m_systemChunkSize = desc.m_heap.m_systemChunkSize;
|
||||
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_MALLOC
|
||||
MallocSchema::Descriptor heapDesc;
|
||||
#endif
|
||||
if (&AllocatorInstance<SystemAllocator>::Get() == this) // if we are the system allocator
|
||||
{
|
||||
AZ_Assert(!g_isSystemSchemaUsed, "AZ::SystemAllocator MUST be created first! It's the source of all allocations!");
|
||||
|
||||
#if AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HPHA
|
||||
m_allocator = azcreate(HphaSchema, (heapDesc), SystemAllocator);
|
||||
m_allocator = new (&g_systemSchema) HphaSchema(heapDesc);
|
||||
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_MALLOC
|
||||
m_allocator = azcreate(MallocSchema, (heapDesc), SystemAllocator);
|
||||
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HEAP
|
||||
m_allocator = azcreate(HeapSchema, (heapDesc), SystemAllocator);
|
||||
m_allocator = new (&g_systemSchema) MallocSchema(heapDesc);
|
||||
#endif
|
||||
if (m_allocator == nullptr)
|
||||
{
|
||||
isReady = false;
|
||||
g_isSystemSchemaUsed = true;
|
||||
isReady = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
isReady = true;
|
||||
// this class should be inheriting from SystemAllocator
|
||||
AZ_Assert(
|
||||
AllocatorInstance<SystemAllocator>::IsReady(),
|
||||
"System allocator must be created before any other allocator! They allocate from it.");
|
||||
|
||||
#if AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HPHA
|
||||
m_allocator = azcreate(HphaSchema, (heapDesc), SystemAllocator);
|
||||
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_MALLOC
|
||||
m_allocator = azcreate(MallocSchema, (heapDesc), SystemAllocator);
|
||||
#endif
|
||||
if (m_allocator == nullptr)
|
||||
{
|
||||
isReady = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
isReady = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return isReady;
|
||||
}
|
||||
|
||||
return isReady;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Allocate
|
||||
// [9/2/2009]
|
||||
//=========================================================================
|
||||
void
|
||||
SystemAllocator::Destroy()
|
||||
{
|
||||
if (g_isSystemSchemaUsed)
|
||||
//=========================================================================
|
||||
// Allocate
|
||||
// [9/2/2009]
|
||||
//=========================================================================
|
||||
void SystemAllocator::Destroy()
|
||||
{
|
||||
int dummy;
|
||||
(void)dummy;
|
||||
}
|
||||
|
||||
if (!m_isCustom)
|
||||
{
|
||||
if ((void*)m_allocator == (void*)&g_systemSchema)
|
||||
if (g_isSystemSchemaUsed)
|
||||
{
|
||||
int dummy;
|
||||
(void)dummy;
|
||||
}
|
||||
|
||||
if (!m_isCustom)
|
||||
{
|
||||
if ((void*)m_allocator == (void*)&g_systemSchema)
|
||||
{
|
||||
#if AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HPHA
|
||||
static_cast<HphaSchema*>(m_allocator)->~HphaSchema();
|
||||
static_cast<HphaSchema*>(m_allocator)->~HphaSchema();
|
||||
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_MALLOC
|
||||
static_cast<MallocSchema*>(m_allocator)->~MallocSchema();
|
||||
#elif AZCORE_SYSTEM_ALLOCATOR == AZCORE_SYSTEM_ALLOCATOR_HEAP
|
||||
static_cast<HeapSchema*>(m_allocator)->~HeapSchema();
|
||||
static_cast<MallocSchema*>(m_allocator)->~MallocSchema();
|
||||
#endif
|
||||
g_isSystemSchemaUsed = false;
|
||||
g_isSystemSchemaUsed = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
azdestroy(m_allocator);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if (m_ownsOSAllocator)
|
||||
{
|
||||
azdestroy(m_allocator);
|
||||
AllocatorInstance<OSAllocator>::Destroy();
|
||||
m_ownsOSAllocator = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_ownsOSAllocator)
|
||||
AllocatorDebugConfig SystemAllocator::GetDebugConfig()
|
||||
{
|
||||
AllocatorInstance<OSAllocator>::Destroy();
|
||||
m_ownsOSAllocator = false;
|
||||
}
|
||||
}
|
||||
|
||||
AllocatorDebugConfig SystemAllocator::GetDebugConfig()
|
||||
{
|
||||
return AllocatorDebugConfig()
|
||||
.StackRecordLevels(m_desc.m_stackRecordLevels)
|
||||
.UsesMemoryGuards(!m_isCustom)
|
||||
.MarksUnallocatedMemory(!m_isCustom)
|
||||
.ExcludeFromDebugging(!m_desc.m_allocationRecords);
|
||||
}
|
||||
|
||||
IAllocatorAllocate* SystemAllocator::GetSchema()
|
||||
{
|
||||
return m_allocator;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Allocate
|
||||
// [9/2/2009]
|
||||
//=========================================================================
|
||||
SystemAllocator::pointer_type
|
||||
SystemAllocator::Allocate(size_type byteSize, size_type alignment, int flags, const char* name, const char* fileName, int lineNum, unsigned int suppressStackRecord)
|
||||
{
|
||||
if (byteSize == 0)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
AZ_Assert(byteSize > 0, "You can not allocate 0 bytes!");
|
||||
AZ_Assert((alignment & (alignment - 1)) == 0, "Alignment must be power of 2!");
|
||||
|
||||
byteSize = MemorySizeAdjustedUp(byteSize);
|
||||
SystemAllocator::pointer_type address = m_allocator->Allocate(byteSize, alignment, flags, name, fileName, lineNum, suppressStackRecord + 1);
|
||||
|
||||
if (address == nullptr)
|
||||
{
|
||||
// Free all memory we can and try again!
|
||||
AllocatorManager::Instance().GarbageCollect();
|
||||
|
||||
address = m_allocator->Allocate(byteSize, alignment, flags, name, fileName, lineNum, suppressStackRecord + 1);
|
||||
return AllocatorDebugConfig()
|
||||
.StackRecordLevels(m_desc.m_stackRecordLevels)
|
||||
.UsesMemoryGuards(!m_isCustom)
|
||||
.MarksUnallocatedMemory(!m_isCustom)
|
||||
.ExcludeFromDebugging(!m_desc.m_allocationRecords);
|
||||
}
|
||||
|
||||
if (address == nullptr)
|
||||
IAllocatorAllocate* SystemAllocator::GetSchema()
|
||||
{
|
||||
byteSize = MemorySizeAdjustedDown(byteSize); // restore original size
|
||||
return m_allocator;
|
||||
}
|
||||
|
||||
AZ_Assert(address != nullptr, "SystemAllocator: Failed to allocate %d bytes aligned on %d (flags: 0x%08x) %s : %s (%d)!", byteSize, alignment, flags, name ? name : "(no name)", fileName ? fileName : "(no file name)", lineNum);
|
||||
//=========================================================================
|
||||
// Allocate
|
||||
// [9/2/2009]
|
||||
//=========================================================================
|
||||
SystemAllocator::pointer_type SystemAllocator::Allocate(
|
||||
size_type byteSize,
|
||||
size_type alignment,
|
||||
int flags,
|
||||
const char* name,
|
||||
const char* fileName,
|
||||
int lineNum,
|
||||
unsigned int suppressStackRecord)
|
||||
{
|
||||
if (byteSize == 0)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
AZ_Assert(byteSize > 0, "You can not allocate 0 bytes!");
|
||||
AZ_Assert((alignment & (alignment - 1)) == 0, "Alignment must be power of 2!");
|
||||
|
||||
AZ_PROFILE_MEMORY_ALLOC_EX(MemoryReserved, fileName, lineNum, address, byteSize, name);
|
||||
AZ_MEMORY_PROFILE(ProfileAllocation(address, byteSize, alignment, name, fileName, lineNum, suppressStackRecord + 1));
|
||||
byteSize = MemorySizeAdjustedUp(byteSize);
|
||||
SystemAllocator::pointer_type address =
|
||||
m_allocator->Allocate(byteSize, alignment, flags, name, fileName, lineNum, suppressStackRecord + 1);
|
||||
|
||||
return address;
|
||||
}
|
||||
if (address == nullptr)
|
||||
{
|
||||
// Free all memory we can and try again!
|
||||
AllocatorManager::Instance().GarbageCollect();
|
||||
|
||||
//=========================================================================
|
||||
// DeAllocate
|
||||
// [9/2/2009]
|
||||
//=========================================================================
|
||||
void
|
||||
SystemAllocator::DeAllocate(pointer_type ptr, size_type byteSize, size_type alignment)
|
||||
{
|
||||
byteSize = MemorySizeAdjustedUp(byteSize);
|
||||
AZ_PROFILE_MEMORY_FREE(MemoryReserved, ptr);
|
||||
AZ_MEMORY_PROFILE(ProfileDeallocation(ptr, byteSize, alignment, nullptr));
|
||||
m_allocator->DeAllocate(ptr, byteSize, alignment);
|
||||
}
|
||||
address = m_allocator->Allocate(byteSize, alignment, flags, name, fileName, lineNum, suppressStackRecord + 1);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// ReAllocate
|
||||
// [9/13/2011]
|
||||
//=========================================================================
|
||||
SystemAllocator::pointer_type
|
||||
SystemAllocator::ReAllocate(pointer_type ptr, size_type newSize, size_type newAlignment)
|
||||
{
|
||||
newSize = MemorySizeAdjustedUp(newSize);
|
||||
if (address == nullptr)
|
||||
{
|
||||
byteSize = MemorySizeAdjustedDown(byteSize); // restore original size
|
||||
}
|
||||
|
||||
AZ_MEMORY_PROFILE(ProfileReallocationBegin(ptr, newSize));
|
||||
AZ_PROFILE_MEMORY_FREE(MemoryReserved, ptr);
|
||||
pointer_type newAddress = m_allocator->ReAllocate(ptr, newSize, newAlignment);
|
||||
AZ_PROFILE_MEMORY_ALLOC(MemoryReserved, newAddress, newSize, "SystemAllocator realloc");
|
||||
AZ_MEMORY_PROFILE(ProfileReallocationEnd(ptr, newAddress, newSize, newAlignment));
|
||||
AZ_Assert(
|
||||
address != nullptr, "SystemAllocator: Failed to allocate %d bytes aligned on %d (flags: 0x%08x) %s : %s (%d)!", byteSize,
|
||||
alignment, flags, name ? name : "(no name)", fileName ? fileName : "(no file name)", lineNum);
|
||||
|
||||
return newAddress;
|
||||
}
|
||||
AZ_PROFILE_MEMORY_ALLOC_EX(MemoryReserved, fileName, lineNum, address, byteSize, name);
|
||||
AZ_MEMORY_PROFILE(ProfileAllocation(address, byteSize, alignment, name, fileName, lineNum, suppressStackRecord + 1));
|
||||
|
||||
//=========================================================================
|
||||
// Resize
|
||||
// [8/12/2011]
|
||||
//=========================================================================
|
||||
SystemAllocator::size_type
|
||||
SystemAllocator::Resize(pointer_type ptr, size_type newSize)
|
||||
{
|
||||
newSize = MemorySizeAdjustedUp(newSize);
|
||||
size_type resizedSize = m_allocator->Resize(ptr, newSize);
|
||||
return address;
|
||||
}
|
||||
|
||||
AZ_MEMORY_PROFILE(ProfileResize(ptr, resizedSize));
|
||||
//=========================================================================
|
||||
// DeAllocate
|
||||
// [9/2/2009]
|
||||
//=========================================================================
|
||||
void SystemAllocator::DeAllocate(pointer_type ptr, size_type byteSize, size_type alignment)
|
||||
{
|
||||
byteSize = MemorySizeAdjustedUp(byteSize);
|
||||
AZ_PROFILE_MEMORY_FREE(MemoryReserved, ptr);
|
||||
AZ_MEMORY_PROFILE(ProfileDeallocation(ptr, byteSize, alignment, nullptr));
|
||||
m_allocator->DeAllocate(ptr, byteSize, alignment);
|
||||
}
|
||||
|
||||
return MemorySizeAdjustedDown(resizedSize);
|
||||
}
|
||||
//=========================================================================
|
||||
// ReAllocate
|
||||
// [9/13/2011]
|
||||
//=========================================================================
|
||||
SystemAllocator::pointer_type SystemAllocator::ReAllocate(pointer_type ptr, size_type newSize, size_type newAlignment)
|
||||
{
|
||||
newSize = MemorySizeAdjustedUp(newSize);
|
||||
|
||||
//=========================================================================
|
||||
//
|
||||
// [8/12/2011]
|
||||
//=========================================================================
|
||||
SystemAllocator::size_type
|
||||
SystemAllocator::AllocationSize(pointer_type ptr)
|
||||
{
|
||||
size_type allocSize = MemorySizeAdjustedDown(m_allocator->AllocationSize(ptr));
|
||||
AZ_MEMORY_PROFILE(ProfileReallocationBegin(ptr, newSize));
|
||||
AZ_PROFILE_MEMORY_FREE(MemoryReserved, ptr);
|
||||
pointer_type newAddress = m_allocator->ReAllocate(ptr, newSize, newAlignment);
|
||||
AZ_PROFILE_MEMORY_ALLOC(MemoryReserved, newAddress, newSize, "SystemAllocator realloc");
|
||||
AZ_MEMORY_PROFILE(ProfileReallocationEnd(ptr, newAddress, newSize, newAlignment));
|
||||
|
||||
return allocSize;
|
||||
}
|
||||
return newAddress;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Resize
|
||||
// [8/12/2011]
|
||||
//=========================================================================
|
||||
SystemAllocator::size_type SystemAllocator::Resize(pointer_type ptr, size_type newSize)
|
||||
{
|
||||
newSize = MemorySizeAdjustedUp(newSize);
|
||||
size_type resizedSize = m_allocator->Resize(ptr, newSize);
|
||||
|
||||
AZ_MEMORY_PROFILE(ProfileResize(ptr, resizedSize));
|
||||
|
||||
return MemorySizeAdjustedDown(resizedSize);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
//
|
||||
// [8/12/2011]
|
||||
//=========================================================================
|
||||
SystemAllocator::size_type SystemAllocator::AllocationSize(pointer_type ptr)
|
||||
{
|
||||
size_type allocSize = MemorySizeAdjustedDown(m_allocator->AllocationSize(ptr));
|
||||
|
||||
return allocSize;
|
||||
}
|
||||
|
||||
} // namespace AZ
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#ifndef AZCORE_SYS_ALLOCATOR_H
|
||||
#define AZCORE_SYS_ALLOCATOR_H
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Memory/Memory.h>
|
||||
|
||||
@@ -120,7 +119,5 @@ namespace AZ
|
||||
};
|
||||
}
|
||||
|
||||
#endif // AZCORE_SYS_ALLOCATOR_H
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
@@ -1424,7 +1424,8 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
using namespace AZ;
|
||||
namespace AZ
|
||||
{
|
||||
|
||||
#ifndef AZ_USE_CUSTOM_SCRIPT_BIND
|
||||
|
||||
@@ -2254,6 +2255,7 @@ LUA_API const Node* lua_getDummyNode()
|
||||
}
|
||||
|
||||
#endif // AZ_USE_CUSTOM_SCRIPT_BIND
|
||||
} // namespace AZ
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -5825,7 +5827,6 @@ LUA_API const Node* lua_getDummyNode()
|
||||
AllocatorWrapper<Internal::LuaSystemAllocator> m_luaAllocator;
|
||||
AZStd::thread::id m_ownerThreadId; // Check if Lua methods (including EBus handlers) are called from background threads.
|
||||
};
|
||||
} // namespace AZ
|
||||
|
||||
ScriptContext::ScriptContext(ScriptContextId id, IAllocatorAllocate* allocator, lua_State* nativeContext)
|
||||
{
|
||||
@@ -6116,5 +6117,6 @@ LUA_API const Node* lua_getDummyNode()
|
||||
{
|
||||
return m_impl->ConstructScriptProperty(sdc, valueIndex, name, restrictToPropertyArrays);
|
||||
}
|
||||
} // namespace AZ
|
||||
|
||||
#undef AZ_DBG_NAME_FIXER
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#ifndef AZCORE_SCRIPT_CONTEXT_H
|
||||
#define AZCORE_SCRIPT_CONTEXT_H
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/std/function/function_fwd.h>
|
||||
@@ -1032,4 +1031,3 @@ namespace AZ
|
||||
}
|
||||
|
||||
} // namespace AZ
|
||||
#endif // AZCORE_SCRIPT_CONTEXT_H
|
||||
|
||||
@@ -25,10 +25,8 @@ extern "C" {
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
|
||||
void LuaHook(lua_State* l, lua_Debug* ar);
|
||||
}
|
||||
|
||||
using namespace AZ;
|
||||
|
||||
/**
|
||||
* A temp class that will override the current script context error handler and store the error (without any messages)
|
||||
@@ -599,7 +597,7 @@ static ScriptContextDebug::BreakpointId MakeBreakpointId(const char* sourceName,
|
||||
// LuaHook
|
||||
// [6/28/2012]
|
||||
//=========================================================================
|
||||
void AZ::LuaHook(lua_State* l, lua_Debug* ar)
|
||||
void LuaHook(lua_State* l, lua_Debug* ar)
|
||||
{
|
||||
// Read contexts
|
||||
lua_rawgeti(l, LUA_REGISTRYINDEX, AZ_LUA_SCRIPT_CONTEXT_REF);
|
||||
@@ -1543,4 +1541,6 @@ ScriptContextDebug::SetValue(const DebugValue& sourceValue)
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace AZ
|
||||
|
||||
#endif // #if !defined(AZCORE_EXCLUDE_LUA)
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#ifndef AZCORE_SCRIPT_CONTEXT_DEBUG_H
|
||||
#define AZCORE_SCRIPT_CONTEXT_DEBUG_H
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Script/ScriptContext.h>
|
||||
#include <AzCore/std/functional.h>
|
||||
@@ -213,6 +212,3 @@ namespace AZ
|
||||
ScriptContext& m_context;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // AZCORE_SCRIPT_CONTEXT_DEBUG_H
|
||||
#pragma once
|
||||
|
||||
@@ -31,7 +31,8 @@
|
||||
#include <AzCore/Serialization/Json/RegistrationContext.h>
|
||||
#include <AzCore/std/string/conversions.h>
|
||||
|
||||
using namespace AZ;
|
||||
namespace AZ
|
||||
{
|
||||
|
||||
/**
|
||||
* Script lifecycle:
|
||||
@@ -44,8 +45,7 @@ using namespace AZ;
|
||||
* If the script was loaded by a ScriptComponent, Load will be called once reload is complete.
|
||||
*/
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace LocalTU_ScriptSystemComponent {
|
||||
// Called when a module has already been loaded
|
||||
static int LuaRequireLoadedModule(lua_State* l)
|
||||
{
|
||||
@@ -54,8 +54,10 @@ namespace
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//=========================================================================
|
||||
// ScriptSystemComponent
|
||||
// [5/29/2012]
|
||||
@@ -479,7 +481,7 @@ int ScriptSystemComponent::DefaultRequireHook(lua_State* lua, ScriptContext* con
|
||||
scriptIt->second.m_scriptNames.emplace(module);
|
||||
// Push the value to a closure that will just return it
|
||||
lua_rawgeti(lua, LUA_REGISTRYINDEX, scriptIt->second.m_tableReference);
|
||||
lua_pushcclosure(lua, LuaRequireLoadedModule, 1);
|
||||
lua_pushcclosure(lua, LocalTU_ScriptSystemComponent::LuaRequireLoadedModule, 1);
|
||||
|
||||
// If asset reference already populated, just return now. Otherwise, capture reference
|
||||
if (scriptIt->second.m_scriptAsset.GetId().IsValid())
|
||||
@@ -519,7 +521,7 @@ int ScriptSystemComponent::DefaultRequireHook(lua_State* lua, ScriptContext* con
|
||||
}
|
||||
|
||||
// Push function returning the result
|
||||
lua_pushcclosure(lua, LuaRequireLoadedModule, 1);
|
||||
lua_pushcclosure(lua, LocalTU_ScriptSystemComponent::LuaRequireLoadedModule, 1);
|
||||
|
||||
// Set asset reference on the loaded script
|
||||
scriptIt = container->m_loadedScripts.find(scriptId.m_guid);
|
||||
@@ -565,7 +567,7 @@ int ScriptSystemComponent::InMemoryRequireHook(lua_State* lua, ScriptContext* co
|
||||
scriptIt->second.m_scriptNames.emplace(module);
|
||||
// Push the value to a closure that will just return it
|
||||
lua_rawgeti(lua, LUA_REGISTRYINDEX, scriptIt->second.m_tableReference);
|
||||
lua_pushcclosure(lua, LuaRequireLoadedModule, 1);
|
||||
lua_pushcclosure(lua, LocalTU_ScriptSystemComponent::LuaRequireLoadedModule, 1);
|
||||
|
||||
// If asset reference already populated, just return now. Otherwise, capture reference
|
||||
if (scriptIt->second.m_scriptAsset.GetId().IsValid())
|
||||
@@ -591,7 +593,7 @@ int ScriptSystemComponent::InMemoryRequireHook(lua_State* lua, ScriptContext* co
|
||||
}
|
||||
|
||||
// Push function returning the result
|
||||
lua_pushcclosure(lua, LuaRequireLoadedModule, 1);
|
||||
lua_pushcclosure(lua, LocalTU_ScriptSystemComponent::LuaRequireLoadedModule, 1);
|
||||
|
||||
// Set asset reference on the loaded script
|
||||
scriptIt = container->m_loadedScripts.find(scriptId.m_guid);
|
||||
@@ -996,4 +998,5 @@ void ScriptSystemComponent::Reflect(ReflectContext* reflection)
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AZ
|
||||
#endif // #if !defined(AZCORE_EXCLUDE_LUA)
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#ifndef AZCORE_SCRIPT_SYSTEM_COMPONENT_H
|
||||
#define AZCORE_SCRIPT_SYSTEM_COMPONENT_H
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
@@ -182,6 +181,3 @@ namespace AZ
|
||||
void OnAssetReloaded(Data::Asset<Data::AssetData> asset) override;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // AZCORE_SCRIPT_SYSTEM_COMPONENT_H
|
||||
#pragma once
|
||||
|
||||
@@ -150,7 +150,7 @@ namespace AZ
|
||||
{
|
||||
// Not using InsertTypeId here to avoid needing to create the temporary value and swap it in that call.
|
||||
node.AddMember(rapidjson::StringRef(JsonSerialization::TypeIdFieldIdentifier),
|
||||
StoreTypeName(classData, context), context.GetJsonAllocator());
|
||||
StoreTypeName(classData, classData.m_typeId, context), context.GetJsonAllocator());
|
||||
result = ResultCode(Tasks::WriteValue, Outcomes::Success);
|
||||
}
|
||||
return result.Combine(StoreClass(node, object, defaultObject, classData, context));
|
||||
@@ -531,7 +531,7 @@ namespace AZ
|
||||
return ResolvePointerResult::ContinueProcessing;
|
||||
}
|
||||
|
||||
rapidjson::Value JsonSerializer::StoreTypeName(const SerializeContext::ClassData& classData, JsonSerializerContext& context)
|
||||
rapidjson::Value JsonSerializer::StoreTypeName(const SerializeContext::ClassData& classData, const Uuid& typeId, JsonSerializerContext& context)
|
||||
{
|
||||
rapidjson::Value result;
|
||||
AZStd::vector<Uuid> ids = context.GetSerializeContext()->FindClassId(Crc32(classData.m_name));
|
||||
@@ -544,7 +544,7 @@ namespace AZ
|
||||
// Only write the Uuid for the class if there are multiple classes sharing the same name.
|
||||
// In this case it wouldn't be enough to determine which class needs to be used. The
|
||||
// class name is still added as a comment for be friendlier for users to read.
|
||||
AZStd::string fullName = classData.m_typeId.ToString<AZStd::string>();
|
||||
AZStd::string fullName = typeId.ToString<AZStd::string>();
|
||||
fullName += ' ';
|
||||
fullName += classData.m_name;
|
||||
result.SetString(fullName.c_str(), aznumeric_caster(fullName.size()), context.GetJsonAllocator());
|
||||
@@ -560,7 +560,7 @@ namespace AZ
|
||||
const SerializeContext::ClassData* data = context.GetSerializeContext()->FindClassData(typeId);
|
||||
if (data)
|
||||
{
|
||||
output = JsonSerializer::StoreTypeName(*data, context);
|
||||
output = JsonSerializer::StoreTypeName(*data, typeId, context);
|
||||
return context.Report(Tasks::WriteValue, Outcomes::Success, "Type id successfully stored to json value.");
|
||||
}
|
||||
else
|
||||
@@ -580,7 +580,7 @@ namespace AZ
|
||||
{
|
||||
rapidjson::Value insertedObject(rapidjson::kObjectType);
|
||||
insertedObject.AddMember(
|
||||
rapidjson::StringRef(JsonSerialization::TypeIdFieldIdentifier), StoreTypeName(classData, context),
|
||||
rapidjson::StringRef(JsonSerialization::TypeIdFieldIdentifier), StoreTypeName(classData, classData.m_typeId, context),
|
||||
context.GetJsonAllocator());
|
||||
|
||||
for (auto& element : output.GetObject())
|
||||
|
||||
@@ -79,7 +79,7 @@ namespace AZ
|
||||
const void*& object, const void*& defaultObject, AZStd::any& defaultObjectStorage,
|
||||
const SerializeContext::ClassData*& elementClassData, const AZ::IRttiHelper& rtti, JsonSerializerContext& context);
|
||||
|
||||
static rapidjson::Value StoreTypeName(const SerializeContext::ClassData& classData, JsonSerializerContext& context);
|
||||
static rapidjson::Value StoreTypeName(const SerializeContext::ClassData& classData, const Uuid& typeId, JsonSerializerContext& context);
|
||||
static JsonSerializationResult::ResultCode StoreTypeName(rapidjson::Value& output,
|
||||
const Uuid& typeId, JsonSerializerContext& context);
|
||||
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* 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/Casting/numeric_cast.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
#include <AzCore/Serialization/Json/StackedString.h>
|
||||
#include <AzCore/Serialization/Json/PathSerializer.h>
|
||||
#include <AzCore/std/containers/array.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
|
||||
namespace AZ::JsonPathSerializerInternal
|
||||
{
|
||||
template<typename PathType>
|
||||
static JsonSerializationResult::Result Load(PathType* pathValue, const rapidjson::Value& inputValue,
|
||||
JsonDeserializerContext& context)
|
||||
{
|
||||
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
|
||||
|
||||
AZ_Assert(pathValue, "Expected a valid pointer to load from json value.");
|
||||
|
||||
switch (inputValue.GetType())
|
||||
{
|
||||
case rapidjson::kArrayType:
|
||||
case rapidjson::kObjectType:
|
||||
case rapidjson::kFalseType:
|
||||
case rapidjson::kTrueType:
|
||||
case rapidjson::kNumberType:
|
||||
[[fallthrough]];
|
||||
case rapidjson::kNullType:
|
||||
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
|
||||
"Unsupported type. String values can't be read from arrays, objects or null.");
|
||||
case rapidjson::kStringType:
|
||||
{
|
||||
size_t pathLength = inputValue.GetStringLength();
|
||||
if (pathLength <= pathValue->Native().max_size())
|
||||
{
|
||||
*pathValue = PathType(AZStd::string_view(inputValue.GetString(), pathLength)).LexicallyNormal();
|
||||
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Success, "Successfully read path.");
|
||||
}
|
||||
using UuidString = AZStd::fixed_string<AZ::Uuid::MaxStringBuffer>;
|
||||
using ErrorString = AZStd::fixed_string<256>;
|
||||
return context.Report(JsonSerializationResult::Tasks::ReadField, JSR::Outcomes::Invalid,
|
||||
ErrorString::format("Json string value is too large to fit within path type %s. It needs to be less than %zu code points",
|
||||
azrtti_typeid<PathType>().template ToString<UuidString>().c_str(), pathValue->Native().max_size()));
|
||||
}
|
||||
default:
|
||||
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unknown, "Unknown json type encountered for string value.");
|
||||
}
|
||||
}
|
||||
template<typename PathType>
|
||||
static JsonSerializationResult::Result StoreWithDefault(rapidjson::Value& outputValue, const PathType* pathValue,
|
||||
const PathType* defaultPathValue, JsonSerializerContext& context)
|
||||
{
|
||||
namespace JSR = JsonSerializationResult; // Removes name conflicts in AzCore in uber builds.
|
||||
|
||||
if (context.ShouldKeepDefaults() || defaultPathValue == nullptr || *pathValue != *defaultPathValue)
|
||||
{
|
||||
auto posixPathString = pathValue->AsPosix();
|
||||
outputValue.SetString(posixPathString.c_str(), aznumeric_caster(posixPathString.size()), context.GetJsonAllocator());
|
||||
return context.Report(JSR::Tasks::WriteValue, JSR::Outcomes::Success, "Path successfully stored.");
|
||||
}
|
||||
|
||||
return context.Report(JSR::Tasks::WriteValue, JSR::Outcomes::DefaultsUsed, "Default Path used.");
|
||||
}
|
||||
}
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
AZ_CLASS_ALLOCATOR_IMPL(JsonPathSerializer, SystemAllocator, 0);
|
||||
|
||||
JsonSerializationResult::Result JsonPathSerializer::Load(void* outputValue, const Uuid& outputValueTypeId,
|
||||
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
|
||||
{
|
||||
if (outputValueTypeId == azrtti_typeid<AZ::IO::Path>())
|
||||
{
|
||||
return JsonPathSerializerInternal::Load(reinterpret_cast<AZ::IO::Path*>(outputValue), inputValue,
|
||||
context);
|
||||
}
|
||||
else if (outputValueTypeId == azrtti_typeid<AZ::IO::FixedMaxPath>())
|
||||
{
|
||||
return JsonPathSerializerInternal::Load(reinterpret_cast<AZ::IO::FixedMaxPath*>(outputValue), inputValue,
|
||||
context);
|
||||
}
|
||||
|
||||
using UuidString = AZStd::fixed_string<AZ::Uuid::MaxStringBuffer>;
|
||||
auto errorTypeIdString = outputValueTypeId.ToString<UuidString>();
|
||||
AZ_Assert(false, "Unable to serialize json string"
|
||||
" to a path of type %s", errorTypeIdString.c_str());
|
||||
|
||||
using ErrorString = AZStd::fixed_string<256>;
|
||||
return context.Report(JsonSerializationResult::Tasks::ReadField, JsonSerializationResult::Outcomes::TypeMismatch,
|
||||
ErrorString::format("Output value type ID %s is not a valid Path type", errorTypeIdString.c_str()));
|
||||
}
|
||||
|
||||
JsonSerializationResult::Result JsonPathSerializer::Store(rapidjson::Value& outputValue, const void* inputValue,
|
||||
const void* defaultValue, const Uuid& valueTypeId, JsonSerializerContext& context)
|
||||
{
|
||||
if (valueTypeId == azrtti_typeid<AZ::IO::Path>())
|
||||
{
|
||||
return JsonPathSerializerInternal::StoreWithDefault(outputValue,
|
||||
reinterpret_cast<const AZ::IO::Path*>(inputValue),
|
||||
reinterpret_cast<const AZ::IO::Path*>(defaultValue), context);
|
||||
}
|
||||
else if (valueTypeId == azrtti_typeid<AZ::IO::FixedMaxPath>())
|
||||
{
|
||||
return JsonPathSerializerInternal::StoreWithDefault(outputValue,
|
||||
reinterpret_cast<const AZ::IO::FixedMaxPath*>(inputValue),
|
||||
reinterpret_cast<const AZ::IO::FixedMaxPath*>(defaultValue), context);
|
||||
}
|
||||
|
||||
using UuidString = AZStd::fixed_string<AZ::Uuid::MaxStringBuffer>;
|
||||
auto errorTypeIdString = valueTypeId.ToString<UuidString>();
|
||||
AZ_Assert(false, "Unable to serialize path type %s to a json string",
|
||||
errorTypeIdString.c_str());
|
||||
|
||||
using ErrorString = AZStd::fixed_string<256>;
|
||||
return context.Report(JsonSerializationResult::Tasks::WriteValue, JsonSerializationResult::Outcomes::TypeMismatch,
|
||||
ErrorString::format("Input value type ID %s is not a valid Path type", errorTypeIdString.c_str()));
|
||||
}
|
||||
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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/Serialization/Json/BaseJsonSerializer.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
class JsonPathSerializer
|
||||
: public BaseJsonSerializer
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(JsonPathSerializer, "{F6FBA901-07E0-4F03-A0B6-72A9A6CE1E96}", BaseJsonSerializer);
|
||||
AZ_CLASS_ALLOCATOR_DECL;
|
||||
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
|
||||
JsonDeserializerContext& context) override;
|
||||
JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
|
||||
const Uuid& valueTypeId, JsonSerializerContext& context) override;
|
||||
};
|
||||
} // namespace AZ
|
||||
@@ -45,9 +45,8 @@ namespace AZ
|
||||
}
|
||||
else
|
||||
{
|
||||
SerializerMap::const_iterator serializerIter = m_jsonSerializers.find(typeId);
|
||||
AZ_Assert(serializerIter != m_jsonSerializers.end(), "Attempting to unregister a serializer that has not been registered yet with typeid %s", typeId.ToString<AZStd::string>().c_str());
|
||||
m_jsonSerializers.erase(serializerIter);
|
||||
[[maybe_unused]] size_t erased = m_jsonSerializers.erase(typeId);
|
||||
AZ_Assert(erased == 1, "Attempting to unregister a serializer that has not been registered yet with typeid %s", typeId.ToString<AZStd::string>().c_str());
|
||||
return SerializerBuilder(this, m_jsonSerializers.end());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,11 +23,11 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
void cvar_t_simulationTickDeltaOverride_Changed(const float& value)
|
||||
void cvar_t_simulationTickDeltaOverride_Changed(const int64_t& value)
|
||||
{
|
||||
if (auto* timeSystem = AZ::Interface<ITime>::Get())
|
||||
{
|
||||
timeSystem->SetSimulationTickDeltaOverride(AZ::SecondsToTimeMs(value));
|
||||
timeSystem->SetSimulationTickDeltaOverride(static_cast<AZ::TimeMs>(value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,8 +44,8 @@ namespace AZ
|
||||
AZ_CVAR(float, t_simulationTickScale, 1.0f, cvar_t_simulationTickScale_Changed, AZ::ConsoleFunctorFlags::Null,
|
||||
"A scalar amount to adjust time passage by, 1.0 == realtime, 0.5 == half realtime, 2.0 == doubletime");
|
||||
|
||||
AZ_CVAR(float, t_simulationTickDeltaOverride, 0.0f, cvar_t_simulationTickDeltaOverride_Changed, AZ::ConsoleFunctorFlags::Null,
|
||||
"If > 0, overrides the simulation tick delta time with the provided value (Seconds) and ignores any t_simulationTickScale value.");
|
||||
AZ_CVAR(int64_t, t_simulationTickDeltaOverride, 0, cvar_t_simulationTickDeltaOverride_Changed, AZ::ConsoleFunctorFlags::Null,
|
||||
"If > 0, overrides the simulation tick delta time with the provided value (Milliseconds) and ignores any t_simulationTickScale value.");
|
||||
|
||||
AZ_CVAR(int, t_simulationTickRate, 0, cvar_t_simulationTickRate_Changed, AZ::ConsoleFunctorFlags::Null,
|
||||
"The minimum rate to force the game simulation tick to run. 0 for as fast as possible. 30 = ~33ms, 60 = ~16ms");
|
||||
@@ -176,7 +176,7 @@ namespace AZ
|
||||
if (timeUs != m_simulationTickDeltaOverride)
|
||||
{
|
||||
m_simulationTickDeltaOverride = timeUs;
|
||||
t_simulationTickDeltaOverride = AZ::TimeUsToSeconds(timeUs); //update the cvar
|
||||
t_simulationTickDeltaOverride = static_cast <int64_t>(timeMs); // update the cvar
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -533,6 +533,8 @@ set(FILES
|
||||
Serialization/Json/JsonUtils.cpp
|
||||
Serialization/Json/MapSerializer.h
|
||||
Serialization/Json/MapSerializer.cpp
|
||||
Serialization/Json/PathSerializer.h
|
||||
Serialization/Json/PathSerializer.cpp
|
||||
Serialization/Json/RegistrationContext.h
|
||||
Serialization/Json/RegistrationContext.cpp
|
||||
Serialization/Json/SmartPointerSerializer.h
|
||||
|
||||
@@ -146,6 +146,11 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
PROPERTY COMPILE_DEFINITIONS
|
||||
VALUES AZCORETEST_DLL_NAME=\"$<TARGET_FILE_NAME:AzCore.Tests>\"
|
||||
)
|
||||
ly_add_target_files(
|
||||
TARGETS AzCore.Tests
|
||||
FILES ${CMAKE_CURRENT_SOURCE_DIR}/Tests/Memory/AllocatorBenchmarkRecordings.bin
|
||||
OUTPUT_SUBDIRECTORY Tests/AzCore/Memory
|
||||
)
|
||||
|
||||
endif()
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <AzCore/Math/Matrix3x3.h>
|
||||
#include <AzCore/Math/Transform.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AZTestShared/Math/MathTestHelpers.h>
|
||||
|
||||
using namespace AZ;
|
||||
|
||||
@@ -408,4 +409,118 @@ namespace UnitTest
|
||||
Matrix4x4 m = Matrix4x4::CreateFromQuaternion(rotQuat);
|
||||
AZ_TEST_ASSERT(m.IsClose(rotMatrix));
|
||||
}
|
||||
|
||||
class QuaternionScaledAxisAngleConversionFixture
|
||||
: public ::testing::TestWithParam<AZ::Quaternion>
|
||||
{
|
||||
public:
|
||||
AZ::Quaternion GetAbs(const AZ::Quaternion& in)
|
||||
{
|
||||
// Take the shortest path for quaternions containing rotations bigger than 180.0°.
|
||||
if (in.GetW() < 0.0f)
|
||||
{
|
||||
return -in;
|
||||
}
|
||||
|
||||
return in;
|
||||
}
|
||||
};
|
||||
|
||||
static const AZ::Quaternion RotationRepresentationConversionTestQuats[] =
|
||||
{
|
||||
AZ::Quaternion::CreateIdentity(),
|
||||
-AZ::Quaternion::CreateIdentity(),
|
||||
AZ::Quaternion::CreateRotationX(AZ::Constants::TwoPi),
|
||||
AZ::Quaternion::CreateRotationY(AZ::Constants::Pi),
|
||||
AZ::Quaternion::CreateRotationZ(AZ::Constants::HalfPi),
|
||||
AZ::Quaternion::CreateRotationX(AZ::Constants::QuarterPi),
|
||||
AZ::Quaternion(0.64f, 0.36f, 0.48f, 0.48f),
|
||||
AZ::Quaternion(0.70f, -0.34f, 0.10f, 0.62f),
|
||||
AZ::Quaternion(-0.38f, 0.34f, 0.70f, -0.50f),
|
||||
AZ::Quaternion(0.70f, -0.34f, -0.38f, 0.50f),
|
||||
AZ::Quaternion(0.00f, 0.00f, -0.28f, 0.96f),
|
||||
AZ::Quaternion(0.24f, -0.64f, 0.72f, 0.12f),
|
||||
AZ::Quaternion(-0.66f, 0.62f, 0.42f, 0.06f)
|
||||
};
|
||||
|
||||
TEST_P(QuaternionScaledAxisAngleConversionFixture, ScaledAxisAngleQuatRoundtripTests)
|
||||
{
|
||||
const AZ::Quaternion testQuat = GetAbs(GetParam());
|
||||
|
||||
// Convert test quaternion to scaled axis-angle representation.
|
||||
const AZ::Vector3 scaledAxisAngle = testQuat.ConvertToScaledAxisAngle();
|
||||
|
||||
// Convert the scaled axis-angle back into a quaternion.
|
||||
AZ::Quaternion backFromScaledAxisAngle = AZ::Quaternion::CreateFromScaledAxisAngle(scaledAxisAngle);
|
||||
|
||||
// Compare the original quaternion with the one after the conversion.
|
||||
EXPECT_THAT(testQuat, IsCloseTolerance(backFromScaledAxisAngle, 1e-6f));
|
||||
}
|
||||
|
||||
TEST_P(QuaternionScaledAxisAngleConversionFixture, AxisAngleQuatRoundtripTests)
|
||||
{
|
||||
const AZ::Quaternion testQuat = GetAbs(GetParam());
|
||||
|
||||
// Convert test quaternion to axis-angle representation.
|
||||
AZ::Vector3 axis;
|
||||
float angle;
|
||||
testQuat.ConvertToAxisAngle(axis, angle);
|
||||
|
||||
// Convert the axis-angle back into a quaternion and compare the original quaternion with the one after the conversion.
|
||||
const AZ::Quaternion backFromAxisAngle = AZ::Quaternion::CreateFromAxisAngle(axis, angle);
|
||||
EXPECT_THAT(testQuat, IsCloseTolerance(backFromAxisAngle, 1e-6f));
|
||||
}
|
||||
|
||||
TEST_P(QuaternionScaledAxisAngleConversionFixture, CompareAxisAngleConversionTests)
|
||||
{
|
||||
const AZ::Quaternion testQuat = GetAbs(GetParam());
|
||||
|
||||
// Convert test quaternion to scaled axis-angle representation.
|
||||
const AZ::Vector3 scaledAxisAngle = testQuat.ConvertToScaledAxisAngle();
|
||||
|
||||
// Convert test quaternion to axis-angle representation and scale it manually.
|
||||
AZ::Vector3 axis;
|
||||
float angle;
|
||||
testQuat.ConvertToAxisAngle(axis, angle);
|
||||
|
||||
// Compare the scaled result to the version from the helper that directly converts it to scaled axis-angle.
|
||||
AZ::Vector3 scaledResult = axis*angle;
|
||||
EXPECT_TRUE(scaledResult.IsClose(scaledAxisAngle, 1e-5f));
|
||||
}
|
||||
|
||||
TEST_P(QuaternionScaledAxisAngleConversionFixture, CompareScaledAxisAngleConversionTests)
|
||||
{
|
||||
const AZ::Quaternion testQuat = GetAbs(GetParam());
|
||||
|
||||
// Convert test quaternion to axis-angle representation and scale it manually.
|
||||
AZ::Vector3 axis;
|
||||
float angle;
|
||||
testQuat.ConvertToAxisAngle(axis, angle);
|
||||
AZ::Vector3 scaledResult = axis*angle;
|
||||
|
||||
// Special case handling for identity rotation.
|
||||
AZ::Vector3 axisFromScaledResult = scaledResult.GetNormalized();
|
||||
float angleFromScaledResult = scaledResult.GetLength();
|
||||
if (AZ::IsClose(angleFromScaledResult, 0.0f))
|
||||
{
|
||||
axisFromScaledResult = AZ::Vector3::CreateAxisY();
|
||||
}
|
||||
|
||||
const AZ::Quaternion backFromAxisAngle = AZ::Quaternion::CreateFromAxisAngle(axisFromScaledResult, angleFromScaledResult);
|
||||
EXPECT_THAT(testQuat, IsCloseTolerance(backFromAxisAngle, 1e-6f));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(MATH_Quaternion, QuaternionScaledAxisAngleConversionFixture, ::testing::ValuesIn(RotationRepresentationConversionTestQuats));
|
||||
|
||||
TEST(MATH_Quaternion, ShortestEquivalent)
|
||||
{
|
||||
const AZ::Quaternion testQuat = AZ::Quaternion::CreateRotationX(AZ::Constants::HalfPi * 3.0f);
|
||||
|
||||
AZ::Quaternion absQuat = testQuat;
|
||||
absQuat.ShortestEquivalent();
|
||||
EXPECT_THAT(testQuat.GetShortestEquivalent(), IsCloseTolerance(absQuat, 1e-6f));
|
||||
|
||||
const float angle = absQuat.GetEulerRadians().GetX();
|
||||
EXPECT_THAT(angle, testing::FloatEq(-AZ::Constants::HalfPi));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:281ba03e79ecba90b313a0b17bdba87c57d76b504b6e38d579b5eabd995902cc
|
||||
size 245760
|
||||
@@ -0,0 +1,591 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#if defined(HAVE_BENCHMARK)
|
||||
|
||||
#include <AzCore/PlatformIncl.h>
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
#include <AzCore/RTTI/TypeInfo.h>
|
||||
#include <AzCore/Memory/BestFitExternalMapAllocator.h>
|
||||
#include <AzCore/Memory/HeapSchema.h>
|
||||
#include <AzCore/Memory/HphaSchema.h>
|
||||
#include <AzCore/Memory/MallocSchema.h>
|
||||
#include <AzCore/Memory/OSAllocator.h>
|
||||
#include <AzCore/Memory/PoolSchema.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/std/containers/array.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
namespace Benchmark
|
||||
{
|
||||
namespace Platform
|
||||
{
|
||||
size_t GetProcessMemoryUsageBytes();
|
||||
size_t GetMemorySize(void* memory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test allocator wrapper that redirects the calls to the passed TAllocator by using AZ::AllocatorInstance.
|
||||
/// It also creates/destroys the TAllocator type (to reflect what happens at runtime)
|
||||
/// </summary>
|
||||
/// <typeparam name="TAllocator">Allocator type to wrap</typeparam>
|
||||
template<typename TAllocator>
|
||||
class TestAllocatorWrapper
|
||||
{
|
||||
public:
|
||||
static void SetUp()
|
||||
{
|
||||
AZ::AllocatorInstance<TAllocator>::Create();
|
||||
}
|
||||
|
||||
static void TearDown()
|
||||
{
|
||||
AZ::AllocatorInstance<TAllocator>::Destroy();
|
||||
}
|
||||
|
||||
static void* Allocate(size_t byteSize, size_t alignment)
|
||||
{
|
||||
return AZ::AllocatorInstance<TAllocator>::Get().Allocate(byteSize, alignment);
|
||||
}
|
||||
|
||||
static void DeAllocate(void* ptr, size_t byteSize = 0)
|
||||
{
|
||||
AZ::AllocatorInstance<TAllocator>::Get().DeAllocate(ptr, byteSize);
|
||||
}
|
||||
|
||||
static void* ReAllocate(void* ptr, size_t newSize, size_t newAlignment)
|
||||
{
|
||||
return AZ::AllocatorInstance<TAllocator>::Get().ReAllocate(ptr, newSize, newAlignment);
|
||||
}
|
||||
|
||||
static size_t Resize(void* ptr, size_t newSize)
|
||||
{
|
||||
return AZ::AllocatorInstance<TAllocator>::Get().Resize(ptr, newSize);
|
||||
}
|
||||
|
||||
static void GarbageCollect()
|
||||
{
|
||||
AZ::AllocatorInstance<TAllocator>::Get().GarbageCollect();
|
||||
}
|
||||
|
||||
static size_t NumAllocatedBytes()
|
||||
{
|
||||
return AZ::AllocatorInstance<TAllocator>::Get().NumAllocatedBytes() +
|
||||
AZ::AllocatorInstance<TAllocator>::Get().GetUnAllocatedMemory();
|
||||
}
|
||||
|
||||
static size_t GetSize(void* ptr)
|
||||
{
|
||||
return AZ::AllocatorInstance<TAllocator>::Get().AllocationSize(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Basic allocator used as a baseline. This allocator is the most basic allocation possible with the OS (AZ_OS_MALLOC).
|
||||
/// MallocSchema cannot be used here because it has extra logic that we don't want to use as a baseline.
|
||||
/// </summary>
|
||||
class RawMallocAllocator {};
|
||||
|
||||
template<>
|
||||
class TestAllocatorWrapper<RawMallocAllocator>
|
||||
{
|
||||
public:
|
||||
TestAllocatorWrapper()
|
||||
{
|
||||
s_numAllocatedBytes = 0;
|
||||
}
|
||||
|
||||
static void SetUp()
|
||||
{
|
||||
s_numAllocatedBytes = 0;
|
||||
}
|
||||
|
||||
static void TearDown()
|
||||
{
|
||||
}
|
||||
|
||||
// IAllocatorAllocate
|
||||
static void* Allocate(size_t byteSize, size_t)
|
||||
{
|
||||
s_numAllocatedBytes += byteSize;
|
||||
// Don't pass an alignment since we wont be able to get the memory size without also passing the alignment
|
||||
return AZ_OS_MALLOC(byteSize, 1);
|
||||
}
|
||||
|
||||
static void DeAllocate(void* ptr, size_t = 0)
|
||||
{
|
||||
s_numAllocatedBytes -= Platform::GetMemorySize(ptr);
|
||||
AZ_OS_FREE(ptr);
|
||||
}
|
||||
|
||||
static void* ReAllocate(void* ptr, size_t newSize, size_t)
|
||||
{
|
||||
s_numAllocatedBytes -= Platform::GetMemorySize(ptr);
|
||||
AZ_OS_FREE(ptr);
|
||||
|
||||
s_numAllocatedBytes += newSize;
|
||||
return AZ_OS_MALLOC(newSize, 1);
|
||||
}
|
||||
|
||||
static size_t Resize(void* ptr, size_t newSize)
|
||||
{
|
||||
AZ_UNUSED(ptr);
|
||||
AZ_UNUSED(newSize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void GarbageCollect() {}
|
||||
|
||||
static size_t NumAllocatedBytes()
|
||||
{
|
||||
return s_numAllocatedBytes;
|
||||
}
|
||||
|
||||
static size_t GetSize(void* ptr)
|
||||
{
|
||||
return Platform::GetMemorySize(ptr);
|
||||
}
|
||||
|
||||
private:
|
||||
static size_t s_numAllocatedBytes;
|
||||
};
|
||||
|
||||
size_t TestAllocatorWrapper<RawMallocAllocator>::s_numAllocatedBytes = 0;
|
||||
|
||||
// Some allocator are not fully declared, those we simply setup from the schema
|
||||
class MallocSchemaAllocator : public AZ::SimpleSchemaAllocator<AZ::MallocSchema>
|
||||
{
|
||||
public:
|
||||
AZ_TYPE_INFO(MallocSchemaAllocator, "{3E68224F-E676-402C-8276-CE4B49C05E89}");
|
||||
|
||||
MallocSchemaAllocator()
|
||||
: AZ::SimpleSchemaAllocator<AZ::MallocSchema>("MallocSchemaAllocator", "")
|
||||
{}
|
||||
};
|
||||
|
||||
// We use both this HphaSchemaAllocator and the SystemAllocator configured with Hpha because the SystemAllocator
|
||||
// has extra things
|
||||
class HphaSchemaAllocator : public AZ::SimpleSchemaAllocator<AZ::HphaSchema>
|
||||
{
|
||||
public:
|
||||
AZ_TYPE_INFO(HphaSchemaAllocator, "{6563AB4B-A68E-4499-8C98-D61D640D1F7F}");
|
||||
|
||||
HphaSchemaAllocator()
|
||||
: AZ::SimpleSchemaAllocator<AZ::HphaSchema>("TestHphaSchemaAllocator", "")
|
||||
{}
|
||||
};
|
||||
|
||||
// For the SystemAllocator we inherit so we have a different stack. The SystemAllocator is used globally so we dont want
|
||||
// to get that data affecting the benchmark
|
||||
class TestSystemAllocator : public AZ::SystemAllocator
|
||||
{
|
||||
public:
|
||||
AZ_TYPE_INFO(TestSystemAllocator, "{360D4DAA-D65D-4D5C-A6FA-1A4C5261C35C}");
|
||||
|
||||
TestSystemAllocator()
|
||||
: AZ::SystemAllocator()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// Allocated bytes reported by the allocator
|
||||
static const char* s_counterAllocatorMemory = "Allocator_Memory";
|
||||
|
||||
// Allocated bytes as counted by the benchmark
|
||||
static const char* s_counterBenchmarkMemory = "Benchmark_Memory";
|
||||
|
||||
enum AllocationSize
|
||||
{
|
||||
SMALL,
|
||||
BIG,
|
||||
MIXED,
|
||||
COUNT
|
||||
};
|
||||
|
||||
static const size_t s_kiloByte = 1024;
|
||||
static const size_t s_megaByte = s_kiloByte * s_kiloByte;
|
||||
using AllocationSizeArray = AZStd::array<size_t, 10>;
|
||||
static const AZStd::array<AllocationSizeArray, COUNT> s_allocationSizes = {
|
||||
/* SMALL */ AllocationSizeArray{ 2, 16, 20, 59, 100, 128, 160, 250, 300, 512 },
|
||||
/* BIG */ AllocationSizeArray{ 513, s_kiloByte, 2 * s_kiloByte, 4 * s_kiloByte, 10 * s_kiloByte, 64 * s_kiloByte, 128 * s_kiloByte, 200 * s_kiloByte, s_megaByte, 2 * s_megaByte },
|
||||
/* MIXED */ AllocationSizeArray{ 2, s_kiloByte, 59, 4 * s_kiloByte, 128, 200 * s_kiloByte, 250, s_megaByte, 512, 2 * s_megaByte }
|
||||
};
|
||||
|
||||
template <typename TAllocator>
|
||||
class AllocatorBenchmarkFixture
|
||||
: public ::benchmark::Fixture
|
||||
{
|
||||
protected:
|
||||
using TestAllocatorType = TestAllocatorWrapper<TAllocator>;
|
||||
|
||||
virtual void internalSetUp(const ::benchmark::State& state)
|
||||
{
|
||||
if (state.thread_index == 0) // Only setup in the first thread
|
||||
{
|
||||
TestAllocatorType::SetUp();
|
||||
|
||||
m_allocations.resize(state.threads);
|
||||
for (auto& perThreadAllocations : m_allocations)
|
||||
{
|
||||
perThreadAllocations.resize(state.range(0), nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void internalTearDown(const ::benchmark::State& state)
|
||||
{
|
||||
if (state.thread_index == 0) // Only setup in the first thread
|
||||
{
|
||||
m_allocations.clear();
|
||||
m_allocations.shrink_to_fit();
|
||||
|
||||
TestAllocatorType::TearDown();
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::vector<void*>& GetPerThreadAllocations(size_t threadIndex)
|
||||
{
|
||||
return m_allocations[threadIndex];
|
||||
}
|
||||
|
||||
public:
|
||||
void SetUp(const ::benchmark::State& state) override
|
||||
{
|
||||
internalSetUp(state);
|
||||
}
|
||||
void SetUp(::benchmark::State& state) override
|
||||
{
|
||||
internalSetUp(state);
|
||||
}
|
||||
|
||||
void TearDown(const ::benchmark::State& state) override
|
||||
{
|
||||
internalTearDown(state);
|
||||
}
|
||||
void TearDown(::benchmark::State& state) override
|
||||
{
|
||||
internalTearDown(state);
|
||||
}
|
||||
|
||||
private:
|
||||
AZStd::vector<AZStd::vector<void*>> m_allocations;
|
||||
};
|
||||
|
||||
template <typename TAllocator, AllocationSize TAllocationSize>
|
||||
class AllocationBenchmarkFixture
|
||||
: public AllocatorBenchmarkFixture<TAllocator>
|
||||
{
|
||||
using base = AllocatorBenchmarkFixture<TAllocator>;
|
||||
using TestAllocatorType = typename base::TestAllocatorType;
|
||||
|
||||
public:
|
||||
void Benchmark(benchmark::State& state)
|
||||
{
|
||||
for (auto _ : state)
|
||||
{
|
||||
state.PauseTiming();
|
||||
|
||||
AZStd::vector<void*>& perThreadAllocations = base::GetPerThreadAllocations(state.thread_index);
|
||||
const size_t numberOfAllocations = perThreadAllocations.size();
|
||||
size_t totalAllocationSize = 0;
|
||||
for (size_t allocationIndex = 0; allocationIndex < numberOfAllocations; ++allocationIndex)
|
||||
{
|
||||
const AllocationSizeArray& allocationArray = s_allocationSizes[TAllocationSize];
|
||||
const size_t allocationSize = allocationArray[allocationIndex % allocationArray.size()];
|
||||
totalAllocationSize += allocationSize;
|
||||
|
||||
state.ResumeTiming();
|
||||
perThreadAllocations[allocationIndex] = TestAllocatorType::Allocate(allocationSize, 0);
|
||||
state.PauseTiming();
|
||||
}
|
||||
|
||||
state.counters[s_counterAllocatorMemory] = benchmark::Counter(static_cast<double>(TestAllocatorType::NumAllocatedBytes()), benchmark::Counter::kDefaults);
|
||||
state.counters[s_counterBenchmarkMemory] = benchmark::Counter(static_cast<double>(totalAllocationSize), benchmark::Counter::kDefaults);
|
||||
|
||||
for (size_t allocationIndex = 0; allocationIndex < numberOfAllocations; ++allocationIndex)
|
||||
{
|
||||
const AllocationSizeArray& allocationArray = s_allocationSizes[TAllocationSize];
|
||||
const size_t allocationSize = allocationArray[allocationIndex % allocationArray.size()];
|
||||
TestAllocatorType::DeAllocate(perThreadAllocations[allocationIndex], allocationSize);
|
||||
perThreadAllocations[allocationIndex] = nullptr;
|
||||
}
|
||||
TestAllocatorType::GarbageCollect();
|
||||
|
||||
state.SetItemsProcessed(numberOfAllocations);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TAllocator, AllocationSize TAllocationSize>
|
||||
class DeAllocationBenchmarkFixture
|
||||
: public AllocatorBenchmarkFixture<TAllocator>
|
||||
{
|
||||
using base = AllocatorBenchmarkFixture<TAllocator>;
|
||||
using TestAllocatorType = typename base::TestAllocatorType;
|
||||
|
||||
public:
|
||||
void Benchmark(benchmark::State& state)
|
||||
{
|
||||
for (auto _ : state)
|
||||
{
|
||||
state.PauseTiming();
|
||||
AZStd::vector<void*>& perThreadAllocations = base::GetPerThreadAllocations(state.thread_index);
|
||||
|
||||
const size_t numberOfAllocations = perThreadAllocations.size();
|
||||
size_t totalAllocationSize = 0;
|
||||
for (size_t allocationIndex = 0; allocationIndex < numberOfAllocations; ++allocationIndex)
|
||||
{
|
||||
const AllocationSizeArray& allocationArray = s_allocationSizes[TAllocationSize];
|
||||
const size_t allocationSize = allocationArray[allocationIndex % allocationArray.size()];
|
||||
totalAllocationSize += allocationSize;
|
||||
perThreadAllocations[allocationIndex] = TestAllocatorType::Allocate(allocationSize, 0);
|
||||
}
|
||||
|
||||
for (size_t allocationIndex = 0; allocationIndex < numberOfAllocations; ++allocationIndex)
|
||||
{
|
||||
const AllocationSizeArray& allocationArray = s_allocationSizes[TAllocationSize];
|
||||
const size_t allocationSize = allocationArray[allocationIndex % allocationArray.size()];
|
||||
state.ResumeTiming();
|
||||
TestAllocatorType::DeAllocate(perThreadAllocations[allocationIndex], allocationSize);
|
||||
state.PauseTiming();
|
||||
perThreadAllocations[allocationIndex] = nullptr;
|
||||
}
|
||||
|
||||
state.counters[s_counterAllocatorMemory] = benchmark::Counter(static_cast<double>(TestAllocatorType::NumAllocatedBytes()), benchmark::Counter::kDefaults);
|
||||
state.counters[s_counterBenchmarkMemory] = benchmark::Counter(static_cast<double>(totalAllocationSize), benchmark::Counter::kDefaults);
|
||||
|
||||
state.SetItemsProcessed(numberOfAllocations);
|
||||
|
||||
TestAllocatorType::GarbageCollect();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TAllocator>
|
||||
class RecordedAllocationBenchmarkFixture : public ::benchmark::Fixture
|
||||
{
|
||||
using TestAllocatorType = TestAllocatorWrapper<TAllocator>;
|
||||
|
||||
virtual void internalSetUp()
|
||||
{
|
||||
TestAllocatorType::SetUp();
|
||||
}
|
||||
|
||||
void internalTearDown()
|
||||
{
|
||||
TestAllocatorType::TearDown();
|
||||
}
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct alignas(1) AllocatorOperation
|
||||
{
|
||||
enum OperationType : size_t
|
||||
{
|
||||
ALLOCATE,
|
||||
DEALLOCATE
|
||||
};
|
||||
OperationType m_type : 1;
|
||||
size_t m_size : 28; // Can represent up to 256Mb requests
|
||||
size_t m_alignment : 7; // Can represent up to 128 alignment
|
||||
size_t m_recordId : 28; // Can represent up to 256M simultaneous requests, we reuse ids
|
||||
};
|
||||
#pragma pack(pop)
|
||||
static_assert(sizeof(AllocatorOperation) == 8);
|
||||
|
||||
public:
|
||||
void SetUp(const ::benchmark::State&) override
|
||||
{
|
||||
internalSetUp();
|
||||
}
|
||||
void SetUp(::benchmark::State&) override
|
||||
{
|
||||
internalSetUp();
|
||||
}
|
||||
|
||||
void TearDown(const ::benchmark::State&) override
|
||||
{
|
||||
internalTearDown();
|
||||
}
|
||||
void TearDown(::benchmark::State&) override
|
||||
{
|
||||
internalTearDown();
|
||||
}
|
||||
|
||||
void Benchmark(benchmark::State& state)
|
||||
{
|
||||
for (auto _ : state)
|
||||
{
|
||||
state.PauseTiming();
|
||||
|
||||
AZStd::unordered_map<size_t, void*> pointerRemapping;
|
||||
constexpr size_t allocationOperationCount = 5 * 1024;
|
||||
AZStd::array<AllocatorOperation, allocationOperationCount> m_operations = {};
|
||||
[[maybe_unused]] const size_t operationSize = sizeof(AllocatorOperation);
|
||||
|
||||
size_t totalAllocationSize = 0;
|
||||
size_t itemsProcessed = 0;
|
||||
|
||||
for (size_t i = 0; i < 100; ++i) // play the recording multiple times to get a good stable sample, this way we can keep a smaller recording
|
||||
{
|
||||
AZ::IO::SystemFile file;
|
||||
AZ::IO::FixedMaxPathString filePath = AZ::Utils::GetExecutableDirectory();
|
||||
filePath += "/Tests/AzCore/Memory/AllocatorBenchmarkRecordings.bin";
|
||||
if (!file.Open(filePath.c_str(), AZ::IO::SystemFile::OpenMode::SF_OPEN_READ_ONLY))
|
||||
{
|
||||
return;
|
||||
}
|
||||
size_t elementsRead =
|
||||
file.Read(sizeof(AllocatorOperation) * allocationOperationCount, &m_operations) / sizeof(AllocatorOperation);
|
||||
itemsProcessed += elementsRead;
|
||||
|
||||
while (elementsRead > 0)
|
||||
{
|
||||
for (size_t operationIndex = 0; operationIndex < elementsRead; ++operationIndex)
|
||||
{
|
||||
const AllocatorOperation& operation = m_operations[operationIndex];
|
||||
if (operation.m_type == AllocatorOperation::ALLOCATE)
|
||||
{
|
||||
const auto it = pointerRemapping.emplace(operation.m_recordId, nullptr);
|
||||
if (it.second) // otherwise already allocated
|
||||
{
|
||||
state.ResumeTiming();
|
||||
void* ptr = TestAllocatorType::Allocate(operation.m_size, operation.m_alignment);
|
||||
state.PauseTiming();
|
||||
totalAllocationSize += operation.m_size;
|
||||
it.first->second = ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Doing a resize, dont account for this memory change, this operation is rare and we dont have
|
||||
// the size of the previous allocation
|
||||
state.ResumeTiming();
|
||||
TestAllocatorType::Resize(it.first->second, operation.m_size);
|
||||
state.PauseTiming();
|
||||
}
|
||||
}
|
||||
else // AllocatorOperation::DEALLOCATE:
|
||||
{
|
||||
if (operation.m_recordId)
|
||||
{
|
||||
const auto ptrIt = pointerRemapping.find(operation.m_recordId);
|
||||
if (ptrIt != pointerRemapping.end())
|
||||
{
|
||||
totalAllocationSize -= operation.m_size;
|
||||
state.ResumeTiming();
|
||||
TestAllocatorType::DeAllocate(
|
||||
ptrIt->second,
|
||||
/*operation.m_size*/ 0); // size is not correct after a resize, a 0 size deals with it
|
||||
state.PauseTiming();
|
||||
pointerRemapping.erase(ptrIt);
|
||||
}
|
||||
}
|
||||
else // deallocate(nullptr) are recorded
|
||||
{
|
||||
// Just to account of the call of deallocate(nullptr);
|
||||
state.ResumeTiming();
|
||||
TestAllocatorType::DeAllocate(nullptr, /*operation.m_size*/ 0);
|
||||
state.PauseTiming();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
elementsRead =
|
||||
file.Read(sizeof(AllocatorOperation) * allocationOperationCount, &m_operations) / sizeof(AllocatorOperation);
|
||||
itemsProcessed += elementsRead;
|
||||
}
|
||||
file.Close();
|
||||
|
||||
// Deallocate the remainder (since we stopped the recording middle-game)(there are leaks as well)
|
||||
for (const auto& pointerMapping : pointerRemapping)
|
||||
{
|
||||
state.ResumeTiming();
|
||||
TestAllocatorType::DeAllocate(pointerMapping.second);
|
||||
state.PauseTiming();
|
||||
}
|
||||
itemsProcessed += pointerRemapping.size();
|
||||
pointerRemapping.clear();
|
||||
}
|
||||
|
||||
state.counters[s_counterAllocatorMemory] = benchmark::Counter(static_cast<double>(TestAllocatorType::NumAllocatedBytes()), benchmark::Counter::kDefaults);
|
||||
state.counters[s_counterBenchmarkMemory] = benchmark::Counter(static_cast<double>(totalAllocationSize), benchmark::Counter::kDefaults);
|
||||
|
||||
state.SetItemsProcessed(itemsProcessed);
|
||||
|
||||
TestAllocatorType::GarbageCollect();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// For non-threaded ranges, run 100, 400, 1600 amounts
|
||||
static void RunRanges(benchmark::internal::Benchmark* b)
|
||||
{
|
||||
for (int i = 0; i < 6; i += 2)
|
||||
{
|
||||
b->Arg((1 << i) * 100);
|
||||
}
|
||||
}
|
||||
static void RecordedRunRanges(benchmark::internal::Benchmark* b)
|
||||
{
|
||||
b->Iterations(1);
|
||||
}
|
||||
|
||||
// For threaded ranges, run just 200, multi-threaded will already multiply by thread
|
||||
static void ThreadedRunRanges(benchmark::internal::Benchmark* b)
|
||||
{
|
||||
b->Arg(100);
|
||||
}
|
||||
|
||||
// Test under and over-subscription of threads vs the amount of CPUs available
|
||||
static const unsigned int MaxThreadRange = 2 * AZStd::thread::hardware_concurrency();
|
||||
|
||||
#define BM_REGISTER_TEMPLATE(FIXTURE, TESTNAME, ...) \
|
||||
BENCHMARK_TEMPLATE_DEFINE_F(FIXTURE, TESTNAME, __VA_ARGS__)(benchmark::State& state) { Benchmark(state); } \
|
||||
BENCHMARK_REGISTER_F(FIXTURE, TESTNAME)
|
||||
|
||||
// We test small/big/mixed allocations in single-threaded environments. For multi-threaded environments, we test mixed since
|
||||
// the multi threaded fixture will run multiple passes (1, 2, 4, ... until 2*hardware_concurrency)
|
||||
#define BM_REGISTER_SIZE_FIXTURES(FIXTURE, TESTNAME, ALLOCATORTYPE) \
|
||||
BM_REGISTER_TEMPLATE(FIXTURE, TESTNAME##_SMALL, ALLOCATORTYPE, SMALL)->Apply(RunRanges); \
|
||||
BM_REGISTER_TEMPLATE(FIXTURE, TESTNAME##_BIG, ALLOCATORTYPE, BIG)->Apply(RunRanges); \
|
||||
BM_REGISTER_TEMPLATE(FIXTURE, TESTNAME##_MIXED, ALLOCATORTYPE, MIXED)->Apply(RunRanges); \
|
||||
BM_REGISTER_TEMPLATE(FIXTURE, TESTNAME##_MIXED_THREADED, ALLOCATORTYPE, MIXED)->ThreadRange(2, MaxThreadRange)->Apply(ThreadedRunRanges);
|
||||
|
||||
#define BM_REGISTER_ALLOCATOR(TESTNAME, ALLOCATORTYPE) \
|
||||
namespace BM_##TESTNAME \
|
||||
{ \
|
||||
BM_REGISTER_SIZE_FIXTURES(AllocationBenchmarkFixture, TESTNAME, ALLOCATORTYPE); \
|
||||
BM_REGISTER_SIZE_FIXTURES(DeAllocationBenchmarkFixture, TESTNAME, ALLOCATORTYPE); \
|
||||
BM_REGISTER_TEMPLATE(RecordedAllocationBenchmarkFixture, TESTNAME, ALLOCATORTYPE)->Apply(RecordedRunRanges); \
|
||||
}
|
||||
|
||||
/// Warm up benchmark used to prepare the OS for allocations. Most OS keep allocations for a process somehow
|
||||
/// reserved. So the first allocations run always get a bigger impact in a process. This warm up allocator runs
|
||||
/// all the benchmarks and is just used for the the next allocators to report more consistent results.
|
||||
BM_REGISTER_ALLOCATOR(WarmUpAllocator, RawMallocAllocator);
|
||||
|
||||
BM_REGISTER_ALLOCATOR(RawMallocAllocator, RawMallocAllocator);
|
||||
BM_REGISTER_ALLOCATOR(MallocSchemaAllocator, MallocSchemaAllocator);
|
||||
BM_REGISTER_ALLOCATOR(HphaSchemaAllocator, HphaSchemaAllocator);
|
||||
BM_REGISTER_ALLOCATOR(SystemAllocator, TestSystemAllocator);
|
||||
|
||||
//BM_REGISTER_ALLOCATOR(BestFitExternalMapAllocator, BestFitExternalMapAllocator); // Requires to pre-allocate blocks and cannot work as a general-purpose allocator
|
||||
//BM_REGISTER_ALLOCATOR(HeapSchemaAllocator, TestHeapSchemaAllocator); // Requires to pre-allocate blocks and cannot work as a general-purpose allocator
|
||||
//BM_REGISTER_SCHEMA(PoolSchema); // Requires special alignment requests while allocating
|
||||
|
||||
#undef BM_REGISTER_ALLOCATOR
|
||||
#undef BM_REGISTER_SIZE_FIXTURES
|
||||
#undef BM_REGISTER_TEMPLATE
|
||||
|
||||
} // Benchmark
|
||||
|
||||
#endif // HAVE_BENCHMARK
|
||||
@@ -10,10 +10,6 @@
|
||||
#include <AzCore/Memory/HphaSchema.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
|
||||
#if defined(HAVE_BENCHMARK)
|
||||
#include <benchmark/benchmark.h>
|
||||
#endif // HAVE_BENCHMARK
|
||||
|
||||
class HphaSchema_TestAllocator
|
||||
: public AZ::SimpleSchemaAllocator<AZ::HphaSchema>
|
||||
{
|
||||
@@ -112,87 +108,3 @@ namespace UnitTest
|
||||
HphaSchemaTestFixture,
|
||||
::testing::ValuesIn(s_mixedInstancesParameters));
|
||||
}
|
||||
|
||||
|
||||
#if defined(HAVE_BENCHMARK)
|
||||
namespace Benchmark
|
||||
{
|
||||
class HphaSchemaBenchmarkFixture
|
||||
: public ::benchmark::Fixture
|
||||
{
|
||||
void internalSetUp()
|
||||
{
|
||||
AZ::AllocatorInstance<HphaSchema_TestAllocator>::Create();
|
||||
}
|
||||
|
||||
void internalTearDown()
|
||||
{
|
||||
AZ::AllocatorInstance<HphaSchema_TestAllocator>::Destroy();
|
||||
}
|
||||
|
||||
public:
|
||||
void SetUp(const benchmark::State&) override
|
||||
{
|
||||
internalSetUp();
|
||||
}
|
||||
void SetUp(benchmark::State&) override
|
||||
{
|
||||
internalSetUp();
|
||||
}
|
||||
void TearDown(const benchmark::State&) override
|
||||
{
|
||||
internalTearDown();
|
||||
}
|
||||
void TearDown(benchmark::State&) override
|
||||
{
|
||||
internalTearDown();
|
||||
}
|
||||
|
||||
static void BM_Allocations(benchmark::State& state, const AllocationSizeArray& allocationArray)
|
||||
{
|
||||
AZStd::vector<void*> allocations;
|
||||
while (state.KeepRunning())
|
||||
{
|
||||
state.PauseTiming();
|
||||
const size_t allocationIndex = allocations.size();
|
||||
const size_t allocationSize = allocationArray[allocationIndex % allocationArray.size()];
|
||||
|
||||
state.ResumeTiming();
|
||||
void* allocation = AZ::AllocatorInstance<HphaSchema_TestAllocator>::Get().Allocate(allocationSize, 0);
|
||||
|
||||
state.PauseTiming();
|
||||
allocations.emplace_back(allocation);
|
||||
|
||||
state.ResumeTiming();
|
||||
}
|
||||
|
||||
const size_t numberOfAllocations = allocations.size();
|
||||
state.SetItemsProcessed(numberOfAllocations);
|
||||
|
||||
for (size_t allocationIndex = 0; allocationIndex < numberOfAllocations; ++allocationIndex)
|
||||
{
|
||||
AZ::AllocatorInstance<HphaSchema_TestAllocator>::Get().DeAllocate(allocations[allocationIndex], allocationArray[allocationIndex % allocationArray.size()]);
|
||||
}
|
||||
AZ::AllocatorInstance<HphaSchema_TestAllocator>::Get().GarbageCollect();
|
||||
}
|
||||
};
|
||||
|
||||
// Small allocations, these are allocations that are going to end up in buckets in the HphaSchema
|
||||
BENCHMARK_F(HphaSchemaBenchmarkFixture, SmallAllocations)(benchmark::State& state)
|
||||
{
|
||||
BM_Allocations(state, s_smallAllocationSizes);
|
||||
}
|
||||
|
||||
BENCHMARK_F(HphaSchemaBenchmarkFixture, BigAllocations)(benchmark::State& state)
|
||||
{
|
||||
BM_Allocations(state, s_bigAllocationSizes);
|
||||
}
|
||||
|
||||
BENCHMARK_F(HphaSchemaBenchmarkFixture, MixedAllocations)(benchmark::State& state)
|
||||
{
|
||||
BM_Allocations(state, s_mixedAllocationSizes);
|
||||
}
|
||||
|
||||
|
||||
} // Benchmark
|
||||
#endif // HAVE_BENCHMARK
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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/PlatformIncl.h>
|
||||
#include <AzCore/Debug/Trace.h>
|
||||
|
||||
#include <malloc.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
namespace Benchmark
|
||||
{
|
||||
namespace Platform
|
||||
{
|
||||
size_t GetProcessMemoryUsageBytes()
|
||||
{
|
||||
struct rusage rusage;
|
||||
getrusage(RUSAGE_SELF, &rusage);
|
||||
return rusage.ru_maxrss * 1024L;
|
||||
}
|
||||
|
||||
size_t GetMemorySize(void* memory)
|
||||
{
|
||||
return memory ? malloc_usable_size(memory) : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,4 +8,5 @@
|
||||
|
||||
set(FILES
|
||||
Tests/UtilsTests_Android.cpp
|
||||
Tests/Memory/AllocatorBenchmarks_Android.cpp
|
||||
)
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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/PlatformIncl.h>
|
||||
#include <AzCore/Debug/Trace.h>
|
||||
|
||||
#include <malloc.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
namespace Benchmark
|
||||
{
|
||||
namespace Platform
|
||||
{
|
||||
size_t GetProcessMemoryUsageBytes()
|
||||
{
|
||||
struct rusage rusage;
|
||||
getrusage(RUSAGE_SELF, &rusage);
|
||||
return rusage.ru_maxrss * 1024L;
|
||||
}
|
||||
|
||||
size_t GetMemorySize(void* memory)
|
||||
{
|
||||
return memory ? malloc_usable_size(memory) : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,4 +9,5 @@
|
||||
set(FILES
|
||||
Tests/UtilsTests_Linux.cpp
|
||||
../Common/UnixLike/Tests/UtilsTests_UnixLike.cpp
|
||||
Tests/Memory/AllocatorBenchmarks_Linux.cpp
|
||||
)
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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/PlatformIncl.h>
|
||||
#include <AzCore/Debug/Trace.h>
|
||||
|
||||
#include <malloc/malloc.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
namespace Benchmark
|
||||
{
|
||||
namespace Platform
|
||||
{
|
||||
size_t GetProcessMemoryUsageBytes()
|
||||
{
|
||||
struct rusage rusage;
|
||||
getrusage(RUSAGE_SELF, &rusage);
|
||||
return rusage.ru_maxrss;
|
||||
}
|
||||
|
||||
size_t GetMemorySize(void* memory)
|
||||
{
|
||||
return memory ? malloc_size(memory) : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,4 +9,5 @@
|
||||
set(FILES
|
||||
../Common/Apple/Tests/UtilsTests_Apple.cpp
|
||||
../Common/UnixLike/Tests/UtilsTests_UnixLike.cpp
|
||||
Tests/Memory/AllocatorBenchmarks_Mac.cpp
|
||||
)
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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/PlatformIncl.h>
|
||||
#include <AzCore/Debug/Trace.h>
|
||||
|
||||
#include <malloc.h>
|
||||
#include <psapi.h>
|
||||
|
||||
namespace Benchmark
|
||||
{
|
||||
namespace Platform
|
||||
{
|
||||
size_t GetProcessMemoryUsageBytes()
|
||||
{
|
||||
EmptyWorkingSet(GetCurrentProcess());
|
||||
|
||||
size_t memoryUsage = 0;
|
||||
MEMORY_BASIC_INFORMATION mbi = { 0 };
|
||||
unsigned char* pEndRegion = nullptr;
|
||||
while (sizeof(mbi) == VirtualQuery(pEndRegion, &mbi, sizeof(mbi))) {
|
||||
pEndRegion += mbi.RegionSize;
|
||||
if ((mbi.AllocationProtect & PAGE_READWRITE) && (mbi.State & MEM_COMMIT)) {
|
||||
memoryUsage += mbi.RegionSize;
|
||||
}
|
||||
}
|
||||
return memoryUsage;
|
||||
}
|
||||
|
||||
size_t GetMemorySize(void* memory)
|
||||
{
|
||||
return memory ? _aligned_msize(memory, 1, 0) : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
set(FILES
|
||||
../Common/WinAPI/Tests/UtilsTests_WinAPI.cpp
|
||||
Tests/IO/Streamer/StorageDriveTests_Windows.cpp
|
||||
Tests/Memory/AllocatorBenchmarks_Windows.cpp
|
||||
Tests/Memory/OverrunDetectionAllocator_Windows.cpp
|
||||
Tests/Serialization_Windows.cpp
|
||||
)
|
||||
|
||||
@@ -327,17 +327,13 @@ namespace JsonSerializationTests
|
||||
SerializerWithOneType::Unreflect(m_jsonRegistrationContext.get());
|
||||
}
|
||||
|
||||
#if GTEST_HAS_DEATH_TEST
|
||||
using JsonSerializationDeathTests = JsonRegistrationContextTests;
|
||||
TEST_F(JsonSerializationDeathTests, DoubleUnregisterSerializer_Asserts)
|
||||
TEST_F(JsonRegistrationContextTests, DoubleUnregisterSerializer_Asserts)
|
||||
{
|
||||
ASSERT_DEATH({
|
||||
SerializerWithOneType::Reflect(m_jsonRegistrationContext.get());
|
||||
SerializerWithOneType::Unreflect(m_jsonRegistrationContext.get());
|
||||
SerializerWithOneType::Unreflect(m_jsonRegistrationContext.get());
|
||||
}, ".*"
|
||||
);
|
||||
SerializerWithOneType::Reflect(m_jsonRegistrationContext.get());
|
||||
SerializerWithOneType::Unreflect(m_jsonRegistrationContext.get());
|
||||
AZ_TEST_START_ASSERTTEST;
|
||||
SerializerWithOneType::Unreflect(m_jsonRegistrationContext.get());
|
||||
AZ_TEST_STOP_ASSERTTEST(1);
|
||||
}
|
||||
#endif // GTEST_HAS_DEATH_TEST
|
||||
|
||||
} //namespace JsonSerializationTests
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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/IO/Path/Path.h>
|
||||
#include <AzCore/IO/Path/PathReflect.h>
|
||||
#include <AzCore/Serialization/Json/PathSerializer.h>
|
||||
#include <Tests/Serialization/Json/BaseJsonSerializerFixture.h>
|
||||
#include <Tests/Serialization/Json/JsonSerializerConformityTests.h>
|
||||
|
||||
namespace JsonSerializationTests
|
||||
{
|
||||
template<typename PathType>
|
||||
class PathTestDescription
|
||||
: public JsonSerializerConformityTestDescriptor<PathType>
|
||||
{
|
||||
public:
|
||||
using JsonSerializerConformityTestDescriptor<PathType>::Reflect;
|
||||
void Reflect(AZStd::unique_ptr<AZ::SerializeContext>& serializeContext) override
|
||||
{
|
||||
AZ::IO::PathReflect(serializeContext.get());
|
||||
}
|
||||
void Reflect(AZStd::unique_ptr<AZ::JsonRegistrationContext>& jsonContext) override
|
||||
{
|
||||
AZ::IO::PathReflect(jsonContext.get());
|
||||
}
|
||||
AZStd::shared_ptr<AZ::BaseJsonSerializer> CreateSerializer() override
|
||||
{
|
||||
return AZStd::make_shared<AZ::JsonPathSerializer>();
|
||||
}
|
||||
|
||||
AZStd::shared_ptr<PathType> CreateDefaultInstance() override
|
||||
{
|
||||
return AZStd::make_shared<PathType>();
|
||||
}
|
||||
|
||||
AZStd::shared_ptr<PathType> CreateFullySetInstance() override
|
||||
{
|
||||
return AZStd::make_shared<PathType>("O3DE/Relative/Path");
|
||||
}
|
||||
|
||||
AZStd::string_view GetJsonForFullySetInstance() override
|
||||
{
|
||||
return R"("O3DE/Relative/Path")";
|
||||
}
|
||||
|
||||
void ConfigureFeatures(JsonSerializerConformityTestDescriptorFeatures& features) override
|
||||
{
|
||||
features.EnableJsonType(rapidjson::kStringType);
|
||||
features.m_supportsPartialInitialization = false;
|
||||
features.m_supportsInjection = false;
|
||||
}
|
||||
|
||||
bool AreEqual(const PathType& lhs, const PathType& rhs) override
|
||||
{
|
||||
return lhs == rhs;
|
||||
}
|
||||
};
|
||||
|
||||
using PathConformityTestTypes = ::testing::Types<
|
||||
PathTestDescription<AZ::IO::Path>,
|
||||
PathTestDescription<AZ::IO::FixedMaxPath>
|
||||
>;
|
||||
INSTANTIATE_TYPED_TEST_CASE_P(Path, JsonSerializerConformityTests, PathConformityTestTypes);
|
||||
|
||||
|
||||
class PathSerializerTests
|
||||
: public BaseJsonSerializerFixture
|
||||
{
|
||||
public:
|
||||
AZStd::unique_ptr<AZ::JsonPathSerializer> m_serializer;
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
BaseJsonSerializerFixture::SetUp();
|
||||
m_serializer = AZStd::make_unique<AZ::JsonPathSerializer>();
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
m_serializer.reset();
|
||||
BaseJsonSerializerFixture::TearDown();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(PathSerializerTests, LoadingIntoFixedMaxPath_GreaterThanMaxPathLength_Fails)
|
||||
{
|
||||
AZ::IO::Path testPath;
|
||||
// Fill a path greater than the AZ::IO::MaxPathLength in write it to Json
|
||||
testPath.Native().append(AZ::IO::MaxPathLength + 2, 'a');
|
||||
|
||||
rapidjson::Value loadPathValue;
|
||||
AZ::JsonSerializationResult::ResultCode resultCode = m_serializer->Store(loadPathValue,
|
||||
&testPath, nullptr, azrtti_typeid<AZ::IO::Path>(), *m_jsonSerializationContext);
|
||||
EXPECT_EQ(AZ::JsonSerializationResult::Outcomes::Success, resultCode.GetOutcome());
|
||||
|
||||
AZ::IO::FixedMaxPath resultPath;
|
||||
AZ::JsonSerializationResult::ResultCode result = m_serializer->Load(&resultPath, azrtti_typeid<AZ::IO::FixedMaxPath>(),
|
||||
loadPathValue, *m_jsonDeserializationContext);
|
||||
EXPECT_GE(result.GetOutcome(), AZ::JsonSerializationResult::Outcomes::Invalid);
|
||||
}
|
||||
} // namespace JsonSerializationTests
|
||||
@@ -10,6 +10,77 @@
|
||||
#include <Tests/Serialization/Json/JsonSerializationTests.h>
|
||||
#include <Tests/Serialization/Json/TestCases_Classes.h>
|
||||
#include <Tests/Serialization/Json/TestCases_Pointers.h>
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
template<typename T>
|
||||
struct SerializeGenericTypeInfo<JsonSerializationTests::TemplatedClass<T>>
|
||||
{
|
||||
using ThisType = JsonSerializationTests::TemplatedClass<T>;
|
||||
|
||||
class GenericTemplatedClassInfo : public GenericClassInfo
|
||||
{
|
||||
public:
|
||||
GenericTemplatedClassInfo()
|
||||
: m_classData{ SerializeContext::ClassData::Create<ThisType>(
|
||||
"TemplatedClass", "{CA4ADF74-66E7-4D16-B4AC-F71278C60EC7}", nullptr, nullptr) }
|
||||
{
|
||||
}
|
||||
|
||||
SerializeContext::ClassData* GetClassData() override
|
||||
{
|
||||
return &m_classData;
|
||||
}
|
||||
|
||||
size_t GetNumTemplatedArguments() override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
const Uuid& GetSpecializedTypeId() const override
|
||||
{
|
||||
return m_classData.m_typeId;
|
||||
}
|
||||
|
||||
const Uuid& GetGenericTypeId() const override
|
||||
{
|
||||
return m_classData.m_typeId;
|
||||
}
|
||||
|
||||
const Uuid& GetTemplatedTypeId(size_t element) override
|
||||
{
|
||||
(void)element;
|
||||
return SerializeGenericTypeInfo<T>::GetClassTypeId();
|
||||
}
|
||||
|
||||
void Reflect(SerializeContext* serializeContext) override
|
||||
{
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->RegisterGenericClassInfo(
|
||||
GetSpecializedTypeId(), this, &AZ::AnyTypeInfoConcept<Data::Asset<Data::AssetData>>::CreateAny);
|
||||
serializeContext->RegisterGenericClassInfo(
|
||||
azrtti_typeid<ThisType>(), this,
|
||||
&AZ::AnyTypeInfoConcept<ThisType>::CreateAny);
|
||||
}
|
||||
}
|
||||
|
||||
SerializeContext::ClassData m_classData;
|
||||
};
|
||||
|
||||
using ClassInfoType = GenericTemplatedClassInfo;
|
||||
static ClassInfoType* GetGenericInfo()
|
||||
{
|
||||
return GetCurrentSerializeContextModule().CreateGenericClassInfo<ThisType>();
|
||||
}
|
||||
|
||||
static const Uuid& GetClassTypeId()
|
||||
{
|
||||
return GetGenericInfo()->GetClassData()->m_typeId;
|
||||
}
|
||||
};
|
||||
} // namespace AZ
|
||||
|
||||
namespace JsonSerializationTests
|
||||
{
|
||||
@@ -286,4 +357,32 @@ namespace JsonSerializationTests
|
||||
EXPECT_EQ(Processing::Halted, result.GetProcessing());
|
||||
EXPECT_EQ(Outcomes::Unknown, result.GetOutcome());
|
||||
}
|
||||
|
||||
TEST_F(JsonSerializationTests, StoreTypeId_TemplatedType_StoresUuidWithName)
|
||||
{
|
||||
using namespace AZ;
|
||||
using namespace AZ::JsonSerializationResult;
|
||||
|
||||
m_serializeContext->RegisterGenericType<TemplatedClass<A::Inherited>>();
|
||||
m_serializeContext->RegisterGenericType<TemplatedClass<BaseClass>>();
|
||||
|
||||
Uuid input = azrtti_typeid<TemplatedClass<A::Inherited>>();
|
||||
ResultCode result = JsonSerialization::StoreTypeId(
|
||||
*m_jsonDocument, m_jsonDocument->GetAllocator(), input, AZStd::string_view{}, *m_serializationSettings);
|
||||
|
||||
EXPECT_EQ(Processing::Completed, result.GetProcessing());
|
||||
|
||||
AZStd::string expected =
|
||||
AZStd::string::format(R"("%s TemplatedClass")", azrtti_typeid<TemplatedClass<A::Inherited>>().ToString<AZStd::string>().c_str());
|
||||
Expect_DocStrEq(expected.c_str(), false);
|
||||
|
||||
input = azrtti_typeid<TemplatedClass<BaseClass>>();
|
||||
result = JsonSerialization::StoreTypeId(
|
||||
*m_jsonDocument, m_jsonDocument->GetAllocator(), input, AZStd::string_view{}, *m_serializationSettings);
|
||||
|
||||
expected =
|
||||
AZStd::string::format(R"("%s TemplatedClass")", azrtti_typeid<TemplatedClass<BaseClass>>().ToString<AZStd::string>().c_str());
|
||||
EXPECT_EQ(Processing::Completed, result.GetProcessing());
|
||||
Expect_DocStrEq(expected.c_str(), false);
|
||||
}
|
||||
} // namespace JsonSerializationTests
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
class TimeTests
|
||||
: public AllocatorsFixture
|
||||
class TimeTests : public AllocatorsFixture
|
||||
{
|
||||
public:
|
||||
void SetUp() override
|
||||
@@ -77,4 +76,4 @@ namespace UnitTest
|
||||
int64_t delta = static_cast<int64_t>(timeMs) - static_cast<int64_t>(timeUsToMs);
|
||||
EXPECT_LT(abs(delta), 1);
|
||||
}
|
||||
}
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -111,6 +111,7 @@ set(FILES
|
||||
Serialization/Json/MapSerializerTests.cpp
|
||||
Serialization/Json/MathVectorSerializerTests.cpp
|
||||
Serialization/Json/MathMatrixSerializerTests.cpp
|
||||
Serialization/Json/PathSerializerTests.cpp
|
||||
Serialization/Json/SmartPointerSerializerTests.cpp
|
||||
Serialization/Json/StringSerializerTests.cpp
|
||||
Serialization/Json/TestCases.h
|
||||
@@ -169,6 +170,7 @@ set(FILES
|
||||
Math/Vector3Tests.cpp
|
||||
Math/Vector4PerformanceTests.cpp
|
||||
Math/Vector4Tests.cpp
|
||||
Memory/AllocatorBenchmarks.cpp
|
||||
Memory/AllocatorManager.cpp
|
||||
Memory/HphaSchema.cpp
|
||||
Memory/HphaSchemaErrorDetection.cpp
|
||||
|
||||
@@ -270,8 +270,6 @@ set(FILES
|
||||
Physics/WindBus.h
|
||||
Process/ProcessCommunicator.cpp
|
||||
Process/ProcessCommunicator.h
|
||||
Process/ProcessWatcher.cpp
|
||||
Process/ProcessWatcher.h
|
||||
Process/ProcessCommon_fwd.h
|
||||
Process/ProcessCommunicator.h
|
||||
Process/ProcessWatcher.cpp
|
||||
|
||||
+5
@@ -83,4 +83,9 @@ namespace AzFramework
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
AZStd::string ProcessLauncher::ProcessLaunchInfo::GetCommandLineParametersAsString() const
|
||||
{
|
||||
return AZStd::string{};
|
||||
}
|
||||
} //namespace AzFramework
|
||||
|
||||
@@ -243,7 +243,7 @@ SliderDoubleCombo::SliderDoubleCombo(QWidget* parent)
|
||||
|
||||
InitialiseSliderCombo(this, layout, m_spinbox, m_slider);
|
||||
|
||||
connect(m_slider, &SliderDouble::valueChanged, this, &SliderDoubleCombo::setValue);
|
||||
connect(m_slider, &SliderDouble::valueChanged, this, &SliderDoubleCombo::setValueSlider);
|
||||
connect(m_spinbox, QOverload<double>::of(&DoubleSpinBox::valueChanged), this, &SliderDoubleCombo::setValue);
|
||||
connect(m_slider, &SliderDouble::sliderReleased, this, &SliderDoubleCombo::editingFinished);
|
||||
connect(m_spinbox, &DoubleSpinBox::editingFinished, this, &SliderDoubleCombo::editingFinished);
|
||||
@@ -254,7 +254,7 @@ SliderDoubleCombo::~SliderDoubleCombo()
|
||||
{
|
||||
}
|
||||
|
||||
void SliderDoubleCombo::setValue(double value)
|
||||
void SliderDoubleCombo::setValueSlider(double value)
|
||||
{
|
||||
const bool doEmit = m_value != value;
|
||||
m_value = value;
|
||||
@@ -264,10 +264,34 @@ void SliderDoubleCombo::setValue(double value)
|
||||
|
||||
if (doEmit)
|
||||
{
|
||||
// We don't want to update the slider from setValue as this
|
||||
// causes rounding errors in the tooltip hint.
|
||||
m_fromSlider = true;
|
||||
Q_EMIT valueChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void SliderDoubleCombo::setValue(double value)
|
||||
{
|
||||
const bool doEmit = m_value != value;
|
||||
m_value = value;
|
||||
|
||||
updateSpinBox();
|
||||
if (!m_fromSlider)
|
||||
{
|
||||
updateSlider();
|
||||
|
||||
if (doEmit)
|
||||
{
|
||||
Q_EMIT valueChanged();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_fromSlider = false;
|
||||
}
|
||||
}
|
||||
|
||||
SliderDouble* SliderDoubleCombo::slider() const
|
||||
{
|
||||
return m_slider;
|
||||
|
||||
@@ -151,6 +151,8 @@ namespace AzQtComponents
|
||||
|
||||
//! Sets the current value.
|
||||
void setValue(double value);
|
||||
//! Sets the current value.
|
||||
void setValueSlider(double value);
|
||||
//! Return the current value.
|
||||
Q_REQUIRED_RESULT double value() const;
|
||||
|
||||
@@ -235,5 +237,6 @@ namespace AzQtComponents
|
||||
double m_softMinimum = 0.0;
|
||||
double m_softMaximum = 100.0;
|
||||
double m_value = 0.0;
|
||||
bool m_fromSlider{ false };
|
||||
};
|
||||
} // namespace AzQtComponents
|
||||
|
||||
+4
-1
@@ -144,7 +144,10 @@ namespace AzToolsFramework
|
||||
{
|
||||
emit ClearStringFilter();
|
||||
emit ClearTypeFilter();
|
||||
m_sourceFilterModel->FilterUpdatedSlotImmediate();
|
||||
if (m_sourceFilterModel)
|
||||
{
|
||||
m_sourceFilterModel->FilterUpdatedSlotImmediate();
|
||||
}
|
||||
}
|
||||
|
||||
void AssetBrowserTableView::Update()
|
||||
|
||||
@@ -2666,19 +2666,15 @@ namespace AzToolsFramework
|
||||
if (!isPathSafeForAssets)
|
||||
{
|
||||
// Put an error in the console, so the log files have info about this error, or the user can look up the error after dismissing it.
|
||||
AZStd::string errorMessage = "You can save slices only to your game project folder or the Gems folder. Update the location and try again.\n\n"
|
||||
"You can also review and update your save locations in the AssetProcessorPlatformConfig.ini file.";
|
||||
AZStd::string errorMessage = "You can save slices only to your game project folder or the Gems folder. Update the location and try again.\n\n";
|
||||
AZ_Error("Slice", false, errorMessage.c_str());
|
||||
|
||||
QString learnMoreLink(QObject::tr(""));
|
||||
QString learnMoreDescription(QObject::tr(" <a href='%1'>Learn more</a>").arg(learnMoreLink));
|
||||
|
||||
// Display a pop-up, the logs are easy to miss. This will make sure a user who encounters this error immediately knows their slice save has failed.
|
||||
QMessageBox msgBox(activeWindow);
|
||||
msgBox.setIcon(QMessageBox::Icon::Warning);
|
||||
msgBox.setTextFormat(Qt::RichText);
|
||||
msgBox.setWindowTitle(QObject::tr("Invalid save location"));
|
||||
msgBox.setText(QString("%1 %2").arg(QObject::tr(errorMessage.c_str())).arg(learnMoreDescription));
|
||||
msgBox.setText(QString("%1").arg(QObject::tr(errorMessage.c_str())));
|
||||
msgBox.setStandardButtons(QMessageBox::Cancel | QMessageBox::Retry);
|
||||
msgBox.setDefaultButton(QMessageBox::Retry);
|
||||
const int response = msgBox.exec();
|
||||
@@ -2689,7 +2685,16 @@ namespace AzToolsFramework
|
||||
// so set the suggested save path to a known valid location.
|
||||
if (assetSafeFolders.size() > 0)
|
||||
{
|
||||
retrySavePath = assetSafeFolders[0];
|
||||
QStringList strList = slicePath.split("/");
|
||||
if (strList.size() > 0)
|
||||
{
|
||||
retrySavePath = assetSafeFolders[0] + ("/" + strList[strList.size() - 1]).toUtf8().data();
|
||||
}
|
||||
else
|
||||
{
|
||||
retrySavePath = assetSafeFolders[0];
|
||||
}
|
||||
|
||||
}
|
||||
return SliceSaveResult::Retry;
|
||||
case QMessageBox::Cancel:
|
||||
|
||||
@@ -840,8 +840,6 @@ namespace AzToolsFramework
|
||||
richLabel->setTextFormat(Qt::RichText);
|
||||
}
|
||||
|
||||
richLabel->setText(data);
|
||||
|
||||
richLabel->setGeometry(options.rect);
|
||||
richLabel->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse);
|
||||
richLabel->setPalette(options.palette);
|
||||
|
||||
@@ -1117,6 +1117,8 @@ namespace AzToolsFramework
|
||||
|
||||
m_listModel->SearchStringChanged(filterString);
|
||||
m_proxyModel->UpdateFilter();
|
||||
|
||||
m_gui->m_objectTree->expandAll();
|
||||
}
|
||||
|
||||
void EntityOutlinerWidget::OnFilterChanged(const AzQtComponents::SearchTypeFilterList& activeTypeFilters)
|
||||
|
||||
@@ -74,10 +74,6 @@ namespace UnitTest
|
||||
AZStd::unique_ptr<AzToolsFramework::AssetBrowser::AssetBrowserFilterModel> m_filterModel;
|
||||
AZStd::unique_ptr<AzToolsFramework::AssetBrowser::AssetBrowserTableModel> m_tableModel;
|
||||
|
||||
AZStd::unique_ptr<QAbstractItemModelTester> m_modelTesterAssetBrowser;
|
||||
AZStd::unique_ptr<QAbstractItemModelTester> m_modelTesterFilterModel;
|
||||
AZStd::unique_ptr<QAbstractItemModelTester> m_modelTesterTableModel;
|
||||
|
||||
QVector<int> m_folderIds = { 13, 14, 15 };
|
||||
QVector<int> m_sourceIDs = { 1, 2, 3, 4, 5 };
|
||||
QVector<int> m_productIDs = { 1, 2, 3, 4, 5 };
|
||||
@@ -96,9 +92,6 @@ namespace UnitTest
|
||||
m_filterModel->setSourceModel(m_assetBrowserComponent->GetAssetBrowserModel());
|
||||
m_tableModel->setSourceModel(m_filterModel.get());
|
||||
|
||||
m_modelTesterAssetBrowser = AZStd::make_unique<QAbstractItemModelTester>(m_assetBrowserComponent->GetAssetBrowserModel());
|
||||
m_modelTesterFilterModel = AZStd::make_unique<QAbstractItemModelTester>(m_filterModel.get());
|
||||
m_modelTesterTableModel = AZStd::make_unique<QAbstractItemModelTester>(m_tableModel.get());
|
||||
m_searchWidget = AZStd::make_unique<AzToolsFramework::AssetBrowser::SearchWidget>();
|
||||
|
||||
// Setup String filters
|
||||
@@ -110,10 +103,6 @@ namespace UnitTest
|
||||
|
||||
void AssetBrowserTest::TearDownEditorFixtureImpl()
|
||||
{
|
||||
m_modelTesterAssetBrowser.reset();
|
||||
m_modelTesterFilterModel.reset();
|
||||
m_modelTesterTableModel.reset();
|
||||
|
||||
m_tableModel.reset();
|
||||
m_filterModel.reset();
|
||||
m_assetBrowserComponent->Deactivate();
|
||||
|
||||
Reference in New Issue
Block a user