Merge branch 'development' into Prefabs/ProcessStackAddPrefabBug

This commit is contained in:
AMZN-koppersr
2022-01-05 11:12:57 -08:00
710 changed files with 145396 additions and 19297 deletions
@@ -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);
}
}
}
+18 -4
View File
@@ -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)
+269 -275
View File
@@ -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
+5
View File
@@ -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
@@ -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
)
@@ -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
@@ -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
@@ -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();