File Memory.h¶
File List > Amplitude > Core > Memory.h
Go to the documentation of this file
// Copyright (c) 2021-present Sparky Studios. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#ifndef _AM_CORE_MEMORY_H
#define _AM_CORE_MEMORY_H
#include <SparkyStudios/Audio/Amplitude/Core/Common.h>
#include <memory>
#if !defined(AM_NO_MEMORY_STATS)
#include <atomic>
#include <mutex>
#include <set>
#include <unordered_map>
#endif
#define amMemory SparkyStudios::Audio::Amplitude::MemoryManager::GetInstance()
#define ampoolmalloc(_pool_, _size_) amMemory->Malloc(_pool_, _size_, __FILE__, __LINE__)
#define ampoolmalign(_pool_, _size_, _alignment_) amMemory->Malign(_pool_, _size_, _alignment_, __FILE__, __LINE__)
#define ampoolrealloc(_pool_, _ptr_, _size_) amMemory->Realloc(_pool_, _ptr_, _size_, __FILE__, __LINE__)
#define ampoolrealign(_pool_, _ptr_, _size_, _alignment_) amMemory->Realign(_pool_, _ptr_, _size_, _alignment_, __FILE__, __LINE__)
#define ampoolfree(_pool_, _ptr_) amMemory->Free(_pool_, _ptr_)
#define ammalloc(_size_) ampoolmalloc(SparkyStudios::Audio::Amplitude::eMemoryPoolKind_Default, _size_)
#define ammalign(_size_, _alignment_) ampoolmalign(SparkyStudios::Audio::Amplitude::eMemoryPoolKind_Default, _size_, _alignment_)
#define amrealloc(_ptr_, _size_) ampoolrealloc(SparkyStudios::Audio::Amplitude::eMemoryPoolKind_Default, _ptr_, _size_)
#define amrealign(_ptr_, _size_, _alignment_) \
ampoolrealign(SparkyStudios::Audio::Amplitude::eMemoryPoolKind_Default, _ptr_, _size_, _alignment_)
#define amfree(_ptr_) ampoolfree(SparkyStudios::Audio::Amplitude::eMemoryPoolKind_Default, _ptr_)
#define ampoolnew(_pool_, _type_, ...) new (ampoolmalign(_pool_, sizeof(_type_), alignof(_type_))) _type_(__VA_ARGS__)
#define ampooldelete(_pool_, _type_, _ptr_) \
do \
{ \
_type_* __amp_tmp = (_ptr_); \
if (__amp_tmp != nullptr) \
{ \
__amp_tmp->~_type_(); \
ampoolfree(_pool_, __amp_tmp); \
} \
} while (0)
#define amnew(_type_, ...) ampoolnew(SparkyStudios::Audio::Amplitude::eMemoryPoolKind_Default, _type_, __VA_ARGS__)
#define amdelete(_type_, _ptr_) ampooldelete(SparkyStudios::Audio::Amplitude::eMemoryPoolKind_Default, _type_, _ptr_)
#define ampoolunique(__pool__, __type__, ...) AmUniquePtr<__type__, __pool__>(ampoolnew(__pool__, __type__, __VA_ARGS__))
#define amunique(_type_, ...) ampoolunique(SparkyStudios::Audio::Amplitude::eMemoryPoolKind_Default, _type_, __VA_ARGS__)
#define ampoolshared(__pool__, __type__, ...) AmSharedPtr<__type__, __pool__>::Make(__VA_ARGS__)
#define amshared(_type_, ...) ampoolshared(SparkyStudios::Audio::Amplitude::eMemoryPoolKind_Default, _type_, __VA_ARGS__)
namespace SparkyStudios::Audio::Amplitude
{
enum eMemoryPoolKind : AmUInt8
{
eMemoryPoolKind_Engine,
eMemoryPoolKind_Amplimix,
eMemoryPoolKind_SoundData,
eMemoryPoolKind_Filtering,
eMemoryPoolKind_Codec,
eMemoryPoolKind_IO,
eMemoryPoolKind_Default,
eMemoryPoolKind_COUNT,
};
#if !defined(AM_NO_MEMORY_STATS)
struct AM_API_PUBLIC MemoryPoolStats
{
eMemoryPoolKind pool;
std::atomic<AmSize> maxMemoryUsed{};
std::atomic<AmUInt64> allocCount{};
std::atomic<AmUInt64> freeCount{};
MemoryPoolStats()
: MemoryPoolStats(eMemoryPoolKind_COUNT)
{}
explicit MemoryPoolStats(eMemoryPoolKind pool);
MemoryPoolStats(const MemoryPoolStats& copy);
MemoryPoolStats& operator=(const MemoryPoolStats& other);
};
#endif
class AM_API_PUBLIC MemoryAllocator
{
public:
virtual ~MemoryAllocator() = default;
virtual AmVoidPtr Malloc(eMemoryPoolKind pool, AmSize size) = 0;
virtual AmVoidPtr Realloc(eMemoryPoolKind pool, AmVoidPtr address, AmSize size) = 0;
virtual AmVoidPtr Malign(eMemoryPoolKind pool, AmSize size, AmUInt32 alignment) = 0;
virtual AmVoidPtr Realign(eMemoryPoolKind pool, AmVoidPtr address, AmSize size, AmUInt32 alignment) = 0;
virtual void Free(eMemoryPoolKind pool, AmVoidPtr address) = 0;
virtual AmSize SizeOf(eMemoryPoolKind pool, AmVoidPtr address) = 0;
};
class AM_API_PUBLIC DefaultMemoryAllocator final : public MemoryAllocator
{
public:
DefaultMemoryAllocator(AmUInt32 bucketsCount, AmSize bucketSizeInBytes);
~DefaultMemoryAllocator() override;
AmVoidPtr Malloc(eMemoryPoolKind pool, AmSize size) override;
AmVoidPtr Realloc(eMemoryPoolKind pool, AmVoidPtr address, AmSize size) override;
AmVoidPtr Malign(eMemoryPoolKind pool, AmSize size, AmUInt32 alignment) override;
AmVoidPtr Realign(eMemoryPoolKind pool, AmVoidPtr address, AmSize size, AmUInt32 alignment) override;
void Free(eMemoryPoolKind pool, AmVoidPtr address) override;
AmSize SizeOf(eMemoryPoolKind pool, AmVoidPtr address) override;
private:
/***
* @brief Internal allocator spaces, for each pool.
*
* @internal
*/
void* _allocators[eMemoryPoolKind_COUNT];
};
class AM_API_PUBLIC MemoryManager
{
public:
struct Allocation
{
eMemoryPoolKind pool;
AmVoidPtr address;
AmSize size;
const char* file;
AmUInt32 line;
[[nodiscard]] AM_INLINE explicit operator AmVoidPtr() const
{
return address;
}
[[nodiscard]] AM_INLINE bool operator==(const AmVoidPtr& ptr) const
{
return address == ptr;
}
[[nodiscard]] AM_INLINE bool operator==(const Allocation& other) const
{
return pool == other.pool && address == other.address;
}
[[nodiscard]] AM_INLINE bool operator<(const Allocation& other) const
{
return address < other.address;
}
};
static void Initialize(std::unique_ptr<MemoryAllocator> allocator = nullptr);
static void Deinitialize();
[[maybe_unused]] static bool IsInitialized();
static MemoryManager* GetInstance();
[[nodiscard]] AmVoidPtr Malloc(eMemoryPoolKind pool, AmSize size, const char* file, AmUInt32 line);
[[nodiscard]] AmVoidPtr Malign(eMemoryPoolKind pool, AmSize size, AmUInt32 alignment, const char* file, AmUInt32 line);
[[nodiscard]] AmVoidPtr Realloc(eMemoryPoolKind pool, AmVoidPtr address, AmSize size, const char* file, AmUInt32 line);
[[nodiscard]] AmVoidPtr Realign(
eMemoryPoolKind pool, AmVoidPtr address, AmSize size, AmUInt32 alignment, const char* file, AmUInt32 line);
void Free(eMemoryPoolKind pool, AmVoidPtr address);
[[nodiscard]] AmSize SizeOf(eMemoryPoolKind pool, AmVoidPtr address) const;
#if !defined(AM_NO_MEMORY_STATS)
[[nodiscard]] AmSize TotalReservedMemorySize(eMemoryPoolKind pool) const;
[[nodiscard]] AmSize TotalReservedMemorySize() const;
static AmString GetMemoryPoolName(eMemoryPoolKind pool);
[[nodiscard]] const MemoryPoolStats& GetStats(eMemoryPoolKind pool) const;
[[nodiscard]] AmString InspectMemoryLeaks() const;
#endif
private:
explicit MemoryManager(std::unique_ptr<MemoryAllocator> allocator);
~MemoryManager();
std::unique_ptr<MemoryAllocator> _allocator;
#if !defined(AM_NO_MEMORY_STATS)
void RemoveAllocation(const Allocation& allocation);
void AddAllocation(const Allocation& allocation);
mutable std::mutex _allocationsMutex;
std::set<Allocation> _memAllocations;
std::unordered_map<eMemoryPoolKind, MemoryPoolStats> _memPoolsStats;
#endif
};
class AM_API_PUBLIC ScopedMemoryAllocation
{
public:
ScopedMemoryAllocation() = default;
ScopedMemoryAllocation(eMemoryPoolKind pool, AmSize size, const char* file, AmUInt32 line);
ScopedMemoryAllocation(eMemoryPoolKind pool, AmSize size, AmUInt32 alignment, const char* file, AmUInt32 line);
ScopedMemoryAllocation(const ScopedMemoryAllocation&) = delete;
ScopedMemoryAllocation& operator=(const ScopedMemoryAllocation&) = delete;
ScopedMemoryAllocation(ScopedMemoryAllocation&& other) noexcept;
ScopedMemoryAllocation& operator=(ScopedMemoryAllocation&& other) noexcept;
~ScopedMemoryAllocation();
template<typename T>
[[nodiscard]] AM_INLINE T* PointerOf() const
{
return static_cast<T*>(_address);
}
template<typename T, typename std::enable_if_t<std::is_pointer_v<T>, bool> = false>
[[nodiscard]] AM_INLINE T As() const
{
return reinterpret_cast<T>(_address);
}
[[nodiscard]] AM_INLINE AmVoidPtr Address() const
{
return _address;
}
private:
eMemoryPoolKind _pool = eMemoryPoolKind_Default;
void* _address = nullptr;
};
template<class T, eMemoryPoolKind Pool = eMemoryPoolKind_Default>
struct am_delete
{
constexpr am_delete() noexcept = default;
AM_INLINE void operator()(T* ptr) const noexcept
{
static_assert(!std::is_void_v<T>, "Cannot delete a void pointer.");
ampooldelete(Pool, T, ptr);
}
};
template<class T, eMemoryPoolKind Pool = eMemoryPoolKind_Default>
using AmUniquePtr = std::unique_ptr<T, am_delete<T, Pool>>;
template<class T, eMemoryPoolKind Pool = eMemoryPoolKind_Default>
class AmSharedPtr : public std::shared_ptr<T>
{
public:
template<class... Args>
static AmSharedPtr<T, Pool> Make(Args&&... args)
{
return AmSharedPtr<T, Pool>(ampoolnew(Pool, T, std::forward<Args>(args)...));
}
explicit AmSharedPtr(T* ptr)
: std::shared_ptr<T>(ptr, am_delete<T, Pool>{})
{}
};
template<typename T>
class AmFakeSharedPtr : public std::shared_ptr<T>
{
struct am_fake_delete
{
constexpr am_fake_delete() noexcept = default;
void operator()(T*) const
{}
};
public:
explicit AmFakeSharedPtr(T* ptr)
: std::shared_ptr<T>(ptr, am_fake_delete{})
{}
};
} // namespace SparkyStudios::Audio::Amplitude
#endif // _AM_CORE_MEMORY_H