Havok: Add MemoryRouter, TLS and base object classes

This commit is contained in:
Léo Lam 2021-12-16 19:43:07 +01:00
parent 406a5c06d1
commit 03993b26fe
No known key found for this signature in database
GPG Key ID: 0DF30F9081000741
13 changed files with 473 additions and 13 deletions

View File

@ -103040,12 +103040,12 @@ Address,Quality,Size,Name
0x0000007101585b50,U,000016,
0x0000007101585b60,U,000004,nullsub_5613
0x0000007101585b64,U,000004,nullsub_5614
0x0000007101585b68,U,000008,
0x0000007101585b68,O,000008,_ZNK18hkReferencedObject12getClassTypeEv
0x0000007101585b70,U,000256,
0x0000007101585c70,U,000020,
0x0000007101585c84,U,000120,
0x0000007101585cfc,U,000088,
0x0000007101585d54,U,000020,
0x0000007101585d54,O,000020,_ZNK18hkReferencedObject26deleteThisReferencedObjectEv
0x0000007101585d68,U,000096,
0x0000007101585dc8,U,000168,
0x0000007101585e70,U,000016,

Can't render this file because it is too large.

View File

@ -2,11 +2,23 @@ project(hkStubs CXX ASM)
add_library(hkStubs OBJECT
Havok/Common/Base/hkBase.h
Havok/Common/Base/Container/String/hkStringPtr.h
Havok/Common/Base/Math/hkMath.h
Havok/Common/Base/Math/Vector/hkVector4.h
Havok/Common/Base/Memory/Allocator/hkMemoryAllocator.h
Havok/Common/Base/Memory/Allocator/Lifo/hkLifoAllocator.h
Havok/Common/Base/Memory/Router/hkMemoryRouter.h
Havok/Common/Base/Object/hkBaseObject.h
Havok/Common/Base/Object/hkReferencedObject.cpp
Havok/Common/Base/Object/hkReferencedObject.h
Havok/Common/Base/Thread/Atomic/hkAtomicPrimitives.h
Havok/Common/Base/Thread/Thread/hkThreadLocalData.h
Havok/Common/Base/Types/hkBaseDefs.h
Havok/Common/Base/Types/hkBaseTypes.h
@ -14,6 +26,7 @@ add_library(hkStubs OBJECT
Havok/Physics/Collide/Shape/Convex/hkpConvexShape.h
Havok/Physics/Collide/Shape/Convex/Capsule/hkpCapsuleShape.h
Havok/Physics/Collide/Shape/HeightField/hkpSphereRepShape.h
Havok/Physics/Dynamics/Entity/hkpEntity.h
Havok/Physics/Dynamics/Entity/hkpRigidBody.h
Havok/Physics/Dynamics/World/hkpWorld.cpp
@ -25,3 +38,8 @@ target_compile_options(hkStubs PRIVATE -fno-exceptions)
target_compile_options(hkStubs PRIVATE -fno-strict-aliasing)
target_compile_options(hkStubs PRIVATE -Wno-invalid-offsetof)
target_include_directories(hkStubs PUBLIC ./)
if(NOT TARGET NintendoSDK)
add_subdirectory(../NintendoSDK)
endif()
target_link_libraries(hkStubs PUBLIC NintendoSDK)

View File

@ -0,0 +1,57 @@
#pragma once
#include <Havok/Common/Base/Memory/Allocator/hkMemoryAllocator.h>
class hkLifoAllocator : public hkMemoryAllocator {
public:
explicit hkLifoAllocator(int slabSize = 32 * 1024);
void init(hkMemoryAllocator* slabAllocator, hkMemoryAllocator* largeAllocator,
hkMemoryAllocator* internalAllocator);
/// Optional: [slabAllocator, largeAllocator, internalAllocator]
void quit(hkMemoryAllocator* allocators[3] = nullptr);
void* blockAlloc(int numBytes) override;
/// Inline equivalent of blockAlloc
// TODO: add this if this is actually used by BotW
HK_FORCE_INLINE void* fastBlockAlloc(int numBytes);
void blockFree(void* p, int numBytes) override;
/// Inline equivalent of blockFree
// TODO: add this if this is actually used by BotW
HK_FORCE_INLINE void fastBlockFree(void* p, int numBytes);
void* bufAlloc(int& reqNumBytesInOut) override;
void bufFree(void* p, int numBytes) override;
void* bufRealloc(void* pold, int oldNumBytes, int& reqNumBytesInOut) override;
void getMemoryStatistics(MemoryStatistics& u) const override {}
int getAllocatedSize(const void* obj, int nbytes) const override { return nbytes; }
bool isEmpty() const;
int numExternalAllocations() const;
protected:
void* allocateFromNewSlab(int nbytes);
void popNonLifoFrees();
void slowBlockFree(void*, int numBytesIn);
void insertNonLifoFree(void* p, int nbytes);
class Implementation;
Implementation* m_impl;
const int m_slabSize;
void* m_cur;
void* m_end;
void* m_firstNonLifoEnd;
void* m_cachedEmptySlab;
hkMemoryAllocator* m_slabAllocator;
hkMemoryAllocator* m_largeAllocator;
hkMemoryAllocator* m_internalAllocator;
};

View File

@ -0,0 +1,102 @@
#pragma once
#include <Havok/Common/Base/Types/hkBaseTypes.h>
class hkMemorySnapshot;
template <typename T>
struct hkSizeOfTypeOrVoid {
enum { val = sizeof(T) };
};
template <>
struct hkSizeOfTypeOrVoid<void> {
enum { val = 1 };
};
class hkMemoryAllocator {
public:
using MemoryWalkCallback = void (*)(void* start, hk_size_t size, bool allocated, int pool,
void* param);
struct MemoryStatistics {
static const hkLong INFINITE_SIZE = -1;
hkLong m_allocated = -1;
hkLong m_inUse = -1;
hkLong m_peakInUse = -1;
hkLong m_available = -1;
hkLong m_totalAvailable = -1;
hkLong m_largestBlock = -1;
};
struct ExtendedInterface {
virtual ~ExtendedInterface() = default;
virtual void garbageCollect() = 0;
virtual void incrementalGarbageCollect(int numBlocks) = 0;
virtual hkResult setMemorySoftLimit(hk_size_t maxMemory) = 0;
virtual hk_size_t getMemorySoftLimit() const = 0;
virtual bool canAllocTotal(int numBytes) = 0;
virtual hkResult walkMemory(MemoryWalkCallback callback, void* param) = 0;
virtual hk_size_t getApproxTotalAllocated() const = 0;
virtual void setScrubValues(hkUint32 allocValue, hkUint32 freeValue) = 0;
virtual int addToSnapshot(hkMemorySnapshot& snap, int parentId) = 0;
};
virtual ~hkMemoryAllocator();
virtual void* blockAlloc(int numBytes) = 0;
virtual void blockFree(void* p, int numBytes) = 0;
virtual void* bufAlloc(int& reqNumBytesInOut);
virtual void bufFree(void* p, int numBytes);
virtual void* bufRealloc(void* pold, int oldNumBytes, int& reqNumBytesInOut);
virtual void blockAllocBatch(void** ptrsOut, int numPtrs, int blockSize);
virtual void blockFreeBatch(void** ptrsIn, int numPtrs, int blockSize);
virtual void getMemoryStatistics(MemoryStatistics& u) const = 0;
virtual int getAllocatedSize(const void* obj, int nbytes) const = 0;
virtual void resetPeakMemoryStatistics() {}
virtual ExtendedInterface* getExtendedInterface() { return nullptr; }
template <typename TYPE>
inline TYPE* _bufAlloc(int& reqNumInOut) {
const int SIZE_ELEM = hkSizeOfTypeOrVoid<TYPE>::val;
int n = reqNumInOut * SIZE_ELEM;
void* p = bufAlloc(n);
reqNumInOut = n / SIZE_ELEM;
return static_cast<TYPE*>(p);
}
template <typename TYPE>
inline void _bufFree(void* p, int numElem) {
const int SIZE_ELEM = hkSizeOfTypeOrVoid<TYPE>::val;
bufFree(p, numElem * SIZE_ELEM);
}
template <typename TYPE>
inline TYPE* _bufRealloc(void* pold, int oldNum, int& reqNumInOut) {
const int SIZE_ELEM = hkSizeOfTypeOrVoid<TYPE>::val;
int n = reqNumInOut * SIZE_ELEM;
void* p = bufRealloc(pold, oldNum * SIZE_ELEM, n);
reqNumInOut = n / SIZE_ELEM;
return static_cast<TYPE*>(p);
}
template <typename TYPE>
inline TYPE* _blockAlloc(int n) {
return static_cast<TYPE*>(blockAlloc(n * hkSizeOfTypeOrVoid<TYPE>::val));
}
template <typename TYPE>
inline void _blockFree(TYPE* p, int n) {
blockFree(p, n * hkSizeOfTypeOrVoid<TYPE>::val);
}
enum MemoryState {
MEMORY_STATE_OK,
MEMORY_STATE_OUT_OF_MEMORY,
};
};

View File

@ -0,0 +1,98 @@
#pragma once
#include <Havok/Common/Base/Memory/Allocator/Lifo/hkLifoAllocator.h>
#include <Havok/Common/Base/Memory/Allocator/hkMemoryAllocator.h>
#include <Havok/Common/Base/Thread/Thread/hkThreadLocalData.h>
#include <type_traits>
class hkLifoAllocator;
class hkOstream;
class hkMemoryRouter {
public:
using Stack = hkLifoAllocator;
using Allocator = hkMemoryAllocator;
static void replaceInstance(hkMemoryRouter* m);
static inline hkMemoryRouter& getInstance() { return *getInstancePtr(); }
static inline hkMemoryRouter* getInstancePtr() { return HK_THREAD_LOCAL_GET(s_memoryRouter); }
hkMemoryRouter();
void resetPeakMemoryStatistics();
/// Small thread local allocation in a stack pattern.
inline Stack& stack() { return m_stack; }
/// Temporary allocation, thread local. Often but not always in a stack pattern.
inline Allocator& temp() { return *m_temp; }
inline void setTemp(Allocator* a) { m_temp = a; }
/// Allocation which is likely to persist. Not thread local.
inline Allocator& heap() { return *m_heap; }
inline void setHeap(Allocator* a) { m_heap = a; }
/// Allocation which may break the rules. Not thread local.
inline Allocator& debug() { return *m_debug; }
inline void setDebug(Allocator* a) { m_debug = a; }
inline Allocator& solver() { return *m_solver; }
inline void setSolver(Allocator* a) { m_solver = a; }
inline void* getUserData() const { return m_userData; }
inline void setUserData(void* p) { m_userData = p; }
inline const hkUint32* getRefObjectLocalStore() const { return &m_refObjLocalStore; }
inline hkUint32* getRefObjectLocalStore() { return &m_refObjLocalStore; }
static void* alignedAlloc(Allocator& b, int nbytes, int align);
static void alignedFree(Allocator& b, void* p);
static void* easyAlloc(Allocator& b, int nbytes);
static hk_size_t getEasyAllocSize(Allocator& b, const void* ptr);
static const void* getEasyAllocStartAddress(Allocator& b, const void* ptr);
static void easyFree(Allocator& b, void* p);
protected:
Stack m_stack;
Allocator* m_temp;
Allocator* m_heap;
Allocator* m_debug;
Allocator* m_solver;
void* m_userData;
hkUint32 m_refObjLocalStore;
static HK_THREAD_LOCAL(hkMemoryRouter*) s_memoryRouter;
};
#define HK_DECLARE_CLASS_ALLOCATOR_IMPL(CLASS_TYPE, ALLOCATOR) \
HK_FORCE_INLINE void* operator new(hk_size_t nbytes) { \
if constexpr (std::is_base_of_v<hkReferencedObject, CLASS_TYPE>) { \
return hkMemoryRouter::getInstance().ALLOCATOR().blockAlloc(static_cast<int>(nbytes)); \
} else { \
return hkMemoryRouter::getInstance().ALLOCATOR().blockAlloc(sizeof(CLASS_TYPE)); \
} \
} \
HK_FORCE_INLINE void operator delete(void* p, hk_size_t nbytes) { \
if constexpr (std::is_base_of_v<hkReferencedObject, CLASS_TYPE>) { \
auto* b = static_cast<hkReferencedObject*>(p); \
hkMemoryRouter::getInstance().ALLOCATOR().blockFree( \
p, (b->getMemorySizeAndFlags() == 0xffff) ? static_cast<int>(nbytes) : \
b->getMemorySizeAndFlags()); \
} else { \
hkMemoryRouter::getInstance().ALLOCATOR().blockFree(p, sizeof(CLASS_TYPE)); \
} \
} \
HK_FORCE_INLINE void* operator new(hk_size_t, void* p) { return p; } \
HK_FORCE_INLINE void* operator new[](hk_size_t, void* p) { return p; } \
HK_FORCE_INLINE void operator delete(void*, void*) {} \
HK_FORCE_INLINE void operator delete[](void*, void*) {}
#define HK_DECLARE_CLASS_ALLOCATOR(CLASS_TYPE) HK_DECLARE_CLASS_ALLOCATOR_IMPL(CLASS_TYPE, heap)
#define HK_DECLARE_CLASS_DEBUG_ALLOCATOR(CLASS_TYPE) \
HK_DECLARE_CLASS_ALLOCATOR_IMPL(CLASS_TYPE, debug)

View File

@ -4,5 +4,6 @@
class hkBaseObject {
public:
virtual ~hkBaseObject() {}
virtual ~hkBaseObject() = default;
virtual void __first_virtual_table_function__() {} // NOLINT(bugprone-reserved-identifier)
};

View File

@ -0,0 +1,9 @@
#include <Havok/Common/Base/Object/hkReferencedObject.h>
const void* hkReferencedObject::getClassType() const {
return nullptr;
}
void hkReferencedObject::deleteThisReferencedObject() const {
delete this;
}

View File

@ -1,10 +1,98 @@
#pragma once
#include <Havok/Common/Base/Memory/Router/hkMemoryRouter.h>
#include <Havok/Common/Base/Object/hkBaseObject.h>
#include <Havok/Common/Base/Types/hkBaseDefs.h>
#include <Havok/Common/Base/Types/hkBaseTypes.h>
#include "Havok/Common/Base/Thread/Atomic/hkAtomicPrimitives.h"
class hkReferencedObject : public hkBaseObject {
public:
virtual ~hkReferencedObject() {}
inline hkReferencedObject();
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
explicit hkReferencedObject(hkFinishLoadedObjectFlag flag) {}
/// Dummy copy constructor and copy assignment operator.
inline hkReferencedObject(const hkReferencedObject& other);
inline hkReferencedObject& operator=(const hkReferencedObject& other);
~hkReferencedObject() override = default;
virtual const void* getClassType() const;
inline int getReferenceCount() const;
inline void setReferenceCount(int newRefCount);
inline void addReference() const;
inline void removeReference() const;
inline int getAllocatedSize() const;
inline int getMemorySizeAndFlags() const;
inline void setMemorySizeAndFlags(int newMemSizeAndFlags);
inline void setMemorySizeFlagsAndReferenceCount(int newMemSizeAndFlags, int newRefCount);
protected:
virtual void deleteThisReferencedObject() const;
private:
mutable hkUint32 m_memSizeAndRefCount;
};
inline hkReferencedObject::hkReferencedObject() : m_memSizeAndRefCount(0xFFFF0001) {}
inline hkReferencedObject::hkReferencedObject(const hkReferencedObject& other)
: m_memSizeAndRefCount(0xFFFF0001) {}
inline hkReferencedObject& hkReferencedObject::operator=(const hkReferencedObject& other) {
return *this;
}
inline int hkReferencedObject::getReferenceCount() const {
return int(m_memSizeAndRefCount & 0xFFFF);
}
inline void hkReferencedObject::setReferenceCount(int newRefCount) {
hkAtomic::readModifyWrite(&m_memSizeAndRefCount, [newRefCount](hkUint32 oldVal) {
return (oldVal & 0xFFFF0000) | (newRefCount & 0xFFFF);
});
}
inline void hkReferencedObject::addReference() const {
if (getMemorySizeAndFlags() == 0)
return;
hkAtomic::readModifyWrite(&m_memSizeAndRefCount, [](hkUint32 oldVal) {
return (oldVal & 0xFFFF0000) | (((oldVal & 0xFFFF) + 1) & 0xFFFF);
});
}
inline void hkReferencedObject::removeReference() const {
if (getMemorySizeAndFlags() == 0)
return;
auto newVal = hkAtomic::readModifyWrite(&m_memSizeAndRefCount, [](hkUint32 oldVal) {
return (oldVal & 0xFFFF0000) | (((oldVal & 0xFFFF) - 1) & 0xFFFF);
});
if ((newVal & 0xFFFF) == 0)
deleteThisReferencedObject();
}
inline int hkReferencedObject::getAllocatedSize() const {
return getMemorySizeAndFlags() & 0x7FFF;
}
inline int hkReferencedObject::getMemorySizeAndFlags() const {
return int((m_memSizeAndRefCount >> 16) & 0xFFFF);
}
inline void hkReferencedObject::setMemorySizeAndFlags(int newMemSizeAndFlags) {
hkAtomic::readModifyWrite(&m_memSizeAndRefCount, [=](hkUint32 oldVal) {
return (newMemSizeAndFlags << 16) | (oldVal & 0xFFFF);
});
}
inline void hkReferencedObject::setMemorySizeFlagsAndReferenceCount(int newMemSizeAndFlags,
int newRefCount) {
const hkUint32 newVal = (newMemSizeAndFlags << 16) | (newRefCount & 0xFFFF);
hkAtomic::readModifyWrite(&m_memSizeAndRefCount, [=](auto) { return newVal; });
}

View File

@ -0,0 +1,25 @@
#pragma once
#include <Havok/Common/Base/hkBase.h>
namespace hkAtomic {
/// Weak compare-and-swap.
template <typename T>
HK_FORCE_INLINE bool compareAndSwap(volatile T* address, const T& oldValue, const T& newValue) {
T expected = oldValue;
return __atomic_compare_exchange_n(address, &expected, newValue, false, __ATOMIC_SEQ_CST,
__ATOMIC_SEQ_CST);
}
template <typename T, typename Fn>
HK_FORCE_INLINE T readModifyWrite(volatile T* address, const Fn& getNewValue) {
T oldVal, newVal;
do {
oldVal = *address;
newVal = getNewValue(oldVal);
} while (!hkAtomic::compareAndSwap(address, oldVal, newVal));
return newVal;
}
} // namespace hkAtomic

View File

@ -0,0 +1,20 @@
#pragma once
#include <nn/os.h>
template <typename T>
class hkThreadLocalData {
public:
hkThreadLocalData() { nn::os::AllocateTlsSlot(&m_tls_slot, nullptr); }
~hkThreadLocalData() { nn::os::FreeTlsSlot(m_tls_slot); }
T getData() const { return reinterpret_cast<T>(nn::os::GetTlsValue(m_tls_slot)); }
void setData(T p) { nn::os::SetTlsValue(m_tls_slot, reinterpret_cast<unsigned long>(p)); }
private:
nn::os::TlsSlot m_tls_slot{};
};
#define HK_THREAD_LOCAL(TYPE) hkThreadLocalData<TYPE>
#define HK_THREAD_LOCAL_SET(var, value) var.setData(value)
#define HK_THREAD_LOCAL_GET(var) var.getData()

View File

@ -1,20 +1,27 @@
#pragma once
#include <Havok/Common/Base/Types/hkBaseDefs.h>
#include <cstdint>
using hkFloat32 = float;
using hkDouble64 = double;
using hkReal = hkFloat32;
using hkChar = signed char;
using hkInt8 = signed char;
using hkInt16 = signed short;
using hkInt32 = signed int;
using hkChar = char;
using hkInt8 = std::int8_t;
using hkInt16 = std::int16_t;
using hkInt32 = std::int32_t;
using hkInt64 = std::int64_t;
using hkUchar = unsigned char;
using hkUint8 = unsigned char;
using hkUint16 = unsigned short;
using hkUint32 = unsigned int;
using hkUint8 = std::uint8_t;
using hkUint16 = std::uint16_t;
using hkUint32 = std::uint32_t;
using hkUint64 = std::uint64_t;
using hk_size_t = hkUint64;
using hkLong = long;
using hkUlong = unsigned long;
using hkUint64 = unsigned long long;
using m128 = __attribute((vector_size(16))) float;
class hkHalf {
@ -118,3 +125,36 @@ public:
private:
Storage m_storage;
};
enum hkResultEnum {
HK_SUCCESS = 0,
HK_FAILURE = 1,
};
struct hkResult {
HK_FORCE_INLINE hkResult() {}
HK_FORCE_INLINE hkResult(hkResultEnum b) { m_enum = b; }
HK_FORCE_INLINE hkResult& operator=(hkResultEnum e) {
m_enum = e;
return *this;
}
HK_FORCE_INLINE bool operator==(hkResultEnum e) const { return m_enum == e; }
HK_FORCE_INLINE bool operator!=(hkResultEnum e) const { return m_enum != e; }
HK_FORCE_INLINE bool isSuccess() const { return m_enum ^ HK_FAILURE; }
hkResultEnum m_enum;
};
inline bool operator==(hkResultEnum e, hkResult r) {
return r.m_enum == e;
}
inline bool operator!=(hkResultEnum e, hkResult r) {
return r.m_enum != e;
}
struct hkFinishLoadedObjectFlag {
int m_finishing = 0;
};

View File

@ -7,4 +7,6 @@
#include <Havok/Common/Base/Math/hkMath.h>
#include <Havok/Common/Base/Memory/Router/hkMemoryRouter.h>
#include <Havok/Common/Base/Container/String/hkStringPtr.h>

@ -1 +1 @@
Subproject commit bc92bd8d9adb6791feef492643ddaf08b99985eb
Subproject commit ca3982b344be82ac3578f545c64403ae41589117