diff options
author | chai <chaifix@163.com> | 2019-08-14 22:50:43 +0800 |
---|---|---|
committer | chai <chaifix@163.com> | 2019-08-14 22:50:43 +0800 |
commit | 15740faf9fe9fe4be08965098bbf2947e096aeeb (patch) | |
tree | a730ec236656cc8cab5b13f088adfaed6bb218fb /Runtime/Serialize/Blobification |
Diffstat (limited to 'Runtime/Serialize/Blobification')
-rw-r--r-- | Runtime/Serialize/Blobification/BlobSize.h | 161 | ||||
-rw-r--r-- | Runtime/Serialize/Blobification/BlobTests.cpp | 358 | ||||
-rw-r--r-- | Runtime/Serialize/Blobification/BlobWrite.cpp | 148 | ||||
-rw-r--r-- | Runtime/Serialize/Blobification/BlobWrite.h | 220 | ||||
-rw-r--r-- | Runtime/Serialize/Blobification/BlobWriteTargetSupport.cpp | 31 | ||||
-rw-r--r-- | Runtime/Serialize/Blobification/BlobWriteTargetSupport.h | 6 | ||||
-rw-r--r-- | Runtime/Serialize/Blobification/OffsetPtrTest.cpp | 31 | ||||
-rw-r--r-- | Runtime/Serialize/Blobification/ReduceCopyData.h | 8 | ||||
-rw-r--r-- | Runtime/Serialize/Blobification/offsetptr.h | 257 |
9 files changed, 1220 insertions, 0 deletions
diff --git a/Runtime/Serialize/Blobification/BlobSize.h b/Runtime/Serialize/Blobification/BlobSize.h new file mode 100644 index 0000000..a7aaf6d --- /dev/null +++ b/Runtime/Serialize/Blobification/BlobSize.h @@ -0,0 +1,161 @@ +#ifndef BLOBSIZE_H +#define BLOBSIZE_H + +#include "Runtime/Serialize/TransferFunctions/TransferBase.h" + +struct ReduceCopyData; + +class BlobSize : public TransferBase +{ +private: + + typedef OffsetPtr<void*>::offset_type offset_type; + + size_t m_Size; + bool m_IgnorePtr; + bool m_HasDebugOffsetPtr; + bool m_Use64Ptr; + int m_TargetPlatform; + + std::size_t AlignAddress(std::size_t addr, std::size_t align) + { + return addr + ((~addr + 1U) & (align - 1U)); + } + +public: + + + BlobSize (bool hasDebugOffsetPtr, bool use64BitOffsetPtr) + : m_Size (0) + , m_IgnorePtr (false) + , m_HasDebugOffsetPtr (hasDebugOffsetPtr) + , m_Use64Ptr (use64BitOffsetPtr) { } + + bool NeedsInstanceIDRemapping () { return m_Flags & kNeedsInstanceIDRemapping; } + int GetFlags () { return m_Flags; } + bool IsWritingGameReleaseData () + { + return true; + } + bool IsSerializingForGameRelease () + { + return true; + } + bool IsBuildingTargetPlatform (BuildTargetPlatform platform) + { + if (platform == kBuildAnyPlayerData) + return m_TargetPlatform >= kBuildValidPlayer; + else + return m_TargetPlatform == platform; + } + + BuildTargetPlatform GetBuildingTargetPlatform () { return static_cast<BuildTargetPlatform>(m_TargetPlatform); } + + template<class T> + void Transfer (T& data, const char* name, TransferMetaFlags metaFlag = kNoTransferFlags); + + template<class T> + void TransferWithTypeString (T& data, const char* name, const char* typeName, TransferMetaFlags metaFlag = kNoTransferFlags); + + template<class T> + void TransferBasicData (T& data); + + template<class T> + void TransferPtr (bool isValidPtr, ReduceCopyData* reduceCopyData); + + template<class T> + void TransferSTLStyleArray (T& data, TransferMetaFlags metaFlag = kNoTransferFlags); + + template<class T> + static size_t CalculateSize (T& data, bool hasDebugOffsetPtr, bool use64BitOffsetPtr) + { + BlobSize sizer (hasDebugOffsetPtr, use64BitOffsetPtr); + sizer.Transfer(data, "Base"); + + // size_t size = sizeof(T); + // Assert( sizeof(T) == sizer.m_Size); + + return sizer.m_Size; + } + + template<class> friend class BlobSizeTransferSTLStyleArrayImpl; +}; + + +template<typename T> class BlobSizeTransferSTLStyleArrayImpl +{ +public: + void operator()(T& data, TransferMetaFlags metaFlags, BlobSize& transfer) + { + AssertString ("STL array are not support for BlobWrite"); + } +}; + +template<typename T> class BlobSizeTransferSTLStyleArrayImpl< OffsetPtrArrayTransfer<T> > +{ +public: + void operator()(OffsetPtrArrayTransfer<T>& data, TransferMetaFlags metaFlags, BlobSize& transfer) + { + transfer.m_IgnorePtr = false; + } +}; + +template<typename T, int SIZE> class BlobSizeTransferSTLStyleArrayImpl< StaticArrayTransfer<T, SIZE> > +{ +public: + void operator()(StaticArrayTransfer<T, SIZE>& data, TransferMetaFlags metaFlags, BlobSize& transfer) + { + transfer.m_Size = transfer.AlignAddress(transfer.m_Size, ALIGN_OF(T)); + transfer.m_Size += sizeof(T)*data.size(); + } +}; + + +template<class T> inline +void BlobSize::TransferSTLStyleArray (T& data, TransferMetaFlags metaFlags) +{ + BlobSizeTransferSTLStyleArrayImpl<T> transfer; + transfer(data, metaFlags, *this); +} + +template<class T> inline +void BlobSize::Transfer (T& data, const char* name, TransferMetaFlags) +{ + if (m_IgnorePtr) + { + m_IgnorePtr = false; + return; + } + + m_Size = AlignAddress(m_Size, SerializeTraits<T>::GetAlignOf() ); + + SerializeTraits<T>::Transfer (data, *this); + + m_Size = AlignAddress(m_Size, SerializeTraits<T>::GetAlignOf() ); +} + +template<class T> inline +void BlobSize::TransferWithTypeString (T& data, const char*, const char*, TransferMetaFlags) +{ + SerializeTraits<T>::Transfer (data, *this); +} + +template<class T> inline +void BlobSize::TransferBasicData (T& srcData) +{ + m_Size += sizeof(T); +} + +template<class T> inline +void BlobSize::TransferPtr (bool isValidPtr, ReduceCopyData* reduceCopyData) +{ + m_Size += m_Use64Ptr ? sizeof(SInt64) : sizeof(SInt32); + + if (m_HasDebugOffsetPtr) + m_Size += sizeof(void*); + + if (isValidPtr) + m_IgnorePtr = true; +} + +#endif diff --git a/Runtime/Serialize/Blobification/BlobTests.cpp b/Runtime/Serialize/Blobification/BlobTests.cpp new file mode 100644 index 0000000..8b0a2d3 --- /dev/null +++ b/Runtime/Serialize/Blobification/BlobTests.cpp @@ -0,0 +1,358 @@ +#include "UnityPrefix.h" + +#if ENABLE_UNIT_TESTS && !(UNITY_LINUX && UNITY_64) +#include "External/UnitTest++/src/UnitTest++.h" +#include <limits> +#include "Runtime/Math/Vector3.h" +#include "Runtime/Math/Simd/math.h" +#include "Runtime/mecanim/defs.h" +#include "Runtime/mecanim/types.h" +#include "Runtime/Allocator/MemoryManager.h" +#include "Runtime/Serialize/SerializeTraits.h" +#include "Runtime/Serialize/Blobification/offsetptr.h" +#include "Runtime/Serialize/Blobification/BlobWrite.h" + +template<typename TYPE> TYPE* Construct() +{ + #if ENABLE_MEMORY_MANAGER + void* _where = GetMemoryManager().Allocate (sizeof(TYPE), ALIGN_OF(TYPE), MemLabelId(kMemNewDeleteId, NULL), kAllocateOptionNone, "Construct"); + #else + void* _where = malloc(sizeof(TYPE)); + #endif + return new(_where) TYPE; +} + +template<typename TYPE> TYPE* ConstructArray(size_t num) +{ + #if ENABLE_MEMORY_MANAGER + void* _where = GetMemoryManager().Allocate (sizeof(TYPE)*num, ALIGN_OF(TYPE), MemLabelId(kMemNewDeleteId, NULL), kAllocateOptionNone, "ConstructArray"); + #else + void* _where = malloc(sizeof(TYPE)*num); + #endif + return new(_where) TYPE[num]; +} + +template<typename TYPE> void Destruct(TYPE* p) +{ + #if ENABLE_MEMORY_MANAGER + GetMemoryManager().Deallocate (p, MemLabelId(kMemNewDeleteId, NULL)); + #else + free(p); + #endif +} + + +struct SampleDataA +{ + enum { + kLastIndex = 20 + }; + + int intValue1; + math::float4 float4Value; // (intentially unaligned) + Vector3f vector3; + + mecanim::uint32_t index[kLastIndex]; + + OffsetPtr<float> nullPtr; + + OffsetPtr<float> floatPtr; + + mecanim::uint32_t arraySize; + OffsetPtr<float> array; + + mecanim::uint32_t emptyArraySize; + OffsetPtr<math::float4> emptyArray; + + int intValue2; + + DECLARE_SERIALIZE(SampleData) +}; + +struct SampleData +{ + int intValue1; // 0 + math::float4 float4Value; // 16 (intentially unaligned) + Vector3f vector3; // 32 + + OffsetPtr<float> nullPtr; // 44 + + OffsetPtr<float> floatPtr; // 52 + + mecanim::uint32_t arraySize; // 60 + OffsetPtr<double> array; // 64 + + mecanim::uint32_t emptyArraySize; // 72 + OffsetPtr<math::float4> emptyArray; // 76 + + mecanim::uint32_t sampleDataASize; // 84 + OffsetPtr<SampleDataA> sampleDataA; // 88 + + mecanim::uint32_t sampleDataAHandleSize; // 96 + OffsetPtr<OffsetPtr<SampleDataA> > sampleDataAHandle; // 100 + + int intValue2; // 108 + + DECLARE_SERIALIZE(SampleData) +}; + + + +template<class TransferFunction> inline +void SampleDataA::Transfer(TransferFunction& transfer) +{ + TRANSFER(intValue1); + TRANSFER(float4Value); + TRANSFER(vector3); + + STATIC_ARRAY_TRANSFER(mecanim::uint32_t, index, kLastIndex); + + TRANSFER(nullPtr); + TRANSFER(floatPtr); + + TRANSFER_BLOB_ONLY(arraySize); + MANUAL_ARRAY_TRANSFER2(float, array, arraySize); + + + TRANSFER_BLOB_ONLY(emptyArraySize); + MANUAL_ARRAY_TRANSFER2(math::float4, emptyArray, emptyArraySize); + + TRANSFER(intValue2); +} + + +template<class TransferFunction> inline +void SampleData::Transfer(TransferFunction& transfer) +{ + TRANSFER(intValue1); + TRANSFER(float4Value); + TRANSFER(vector3); + + TRANSFER(nullPtr); + TRANSFER(floatPtr); + + TRANSFER_BLOB_ONLY(arraySize); + MANUAL_ARRAY_TRANSFER2(double, array, arraySize); + + + TRANSFER_BLOB_ONLY(emptyArraySize); + MANUAL_ARRAY_TRANSFER2(math::float4, emptyArray, emptyArraySize); + + TRANSFER_BLOB_ONLY(sampleDataASize); + MANUAL_ARRAY_TRANSFER2(SampleDataA, sampleDataA, sampleDataASize); + + TRANSFER_BLOB_ONLY(sampleDataAHandleSize); + MANUAL_ARRAY_TRANSFER2(OffsetPtr<SampleDataA>, sampleDataAHandle, sampleDataAHandleSize); + + TRANSFER(intValue2); +} + +static void SetupTestDataA (SampleDataA& sourceData) +{ + sourceData.intValue1 = 1; + sourceData.float4Value = math::float4(1, 2, 3, 4); + sourceData.vector3 = Vector3f(1,2,3); + + mecanim::uint32_t i; + for(i=0; i<SampleDataA::kLastIndex;i++) + sourceData.index[i] = i; + + sourceData.nullPtr = NULL; + sourceData.floatPtr = new float; + *sourceData.floatPtr = 5.5F; + + sourceData.emptyArraySize = 0; + sourceData.emptyArray = NULL; + + sourceData.arraySize = 3; + sourceData.array = new float[3]; + sourceData.array[0] = 6.5f; + sourceData.array[1] = 7.5f; + sourceData.array[2] = 8.5f; + sourceData.intValue2 = 2; +} + +static void DeleteTestDataA (SampleDataA& sourceData) +{ + delete[] sourceData.array.Get(); + delete sourceData.floatPtr.Get(); +} + + +static void SetupTestData (SampleData& sourceData) +{ + sourceData.intValue1 = 1; + sourceData.float4Value = math::float4(1, 2, 3, 4); + sourceData.vector3 = Vector3f(1,2,3); + sourceData.nullPtr = NULL; + sourceData.floatPtr = new float; + *sourceData.floatPtr = 5.5F; + + sourceData.emptyArraySize = 0; + sourceData.emptyArray = NULL; + + sourceData.arraySize = 3; + sourceData.array = new double[3]; + sourceData.array[0] = 6.5; + sourceData.array[1] = 7.5; + sourceData.array[2] = 8.5; + sourceData.intValue2 = 2; + + sourceData.sampleDataASize = 4; + + sourceData.sampleDataA = ConstructArray<SampleDataA>(sourceData.sampleDataASize); + for(int i=0;i<sourceData.sampleDataASize;i++) + { + SetupTestDataA(sourceData.sampleDataA[i]); + } + + sourceData.sampleDataAHandleSize = 2; + sourceData.sampleDataAHandle = new OffsetPtr<SampleDataA> [2]; + sourceData.sampleDataAHandle[0] = Construct<SampleDataA>(); + SetupTestDataA(*sourceData.sampleDataAHandle[0]); + sourceData.sampleDataAHandle[1] = Construct<SampleDataA>(); + SetupTestDataA(*sourceData.sampleDataAHandle[1]); +} + + +static void DeleteTestData (SampleData& sourceData) +{ + for(int i=0;i<sourceData.sampleDataASize;i++) + DeleteTestDataA (sourceData.sampleDataA[i]); + + Destruct (sourceData.sampleDataA.Get()); + Destruct (sourceData.sampleDataAHandle[0].Get()); + Destruct (sourceData.sampleDataAHandle[1].Get()); + + delete sourceData.floatPtr.Get(); + delete[] sourceData.array.Get(); + delete[] sourceData.sampleDataAHandle.Get(); +} + + +static void TestDataA (SampleDataA& deserialized) +{ + CHECK (reinterpret_cast<UInt32>(&deserialized) % ALIGN_OF(SampleDataA) == 0 ); + CHECK (deserialized.intValue1 == 1); + CHECK (dot(deserialized.float4Value - math::float4(1, 2, 3, 4)) == math::float1::zero()); + + mecanim::uint32_t i; + for(i=0; i<SampleDataA::kLastIndex;i++) + CHECK (deserialized.index[i] == i); + + CHECK (deserialized.nullPtr.IsNull()); + + float ptr = *deserialized.floatPtr; + CHECK (ptr == 5.5F); + + float* array = deserialized.array.Get(); + CHECK (array[0] == 6.5F); + CHECK (array[1] == 7.5F); + CHECK (array[2] == 8.5F); + + CHECK (deserialized.emptyArray.IsNull()); + CHECK (deserialized.emptyArraySize == 0); + + + CHECK (deserialized.vector3 == Vector3f(1,2,3)); + CHECK (deserialized.intValue2 == 2); +} + + +static void TestData (SampleData& deserialized) +{ + CHECK (reinterpret_cast<UInt32>(&deserialized) % ALIGN_OF(SampleData) == 0); + CHECK (deserialized.intValue1 == 1); + CHECK (dot(deserialized.float4Value - math::float4(1, 2, 3, 4)) == math::float1::zero() ); + + CHECK (deserialized.nullPtr.IsNull()); + + float ptr = *deserialized.floatPtr; + CHECK (ptr == 5.5F); + + double* array = deserialized.array.Get(); + CHECK (array[0] == 6.5); + CHECK (array[1] == 7.5); + CHECK (array[2] == 8.5); + + CHECK (deserialized.emptyArray.IsNull()); + CHECK (deserialized.emptyArraySize == 0); + + CHECK (deserialized.vector3 == Vector3f(1,2,3)); + CHECK (deserialized.intValue2 == 2); + + CHECK (deserialized.sampleDataASize == 4); + for(int i=0;i<deserialized.sampleDataASize;i++) + { + TestDataA(deserialized.sampleDataA[i]); + } + + CHECK (deserialized.sampleDataAHandleSize == 2); + for(int i=0;i<deserialized.sampleDataAHandleSize;i++) + { + TestDataA(*deserialized.sampleDataAHandle[i]); + } +} + +// This test crashes the linux-amd64 editor right now +SUITE (BlobTests) +{ + TEST (Blobification_BlobPtrs) + { + SampleData sourceData; + SetupTestData (sourceData); + TestData(sourceData); + + // Generate blob + BlobWrite::container_type data; + BlobWrite blobWrite (data, kNoTransferInstructionFlags, kBuildNoTargetPlatform); + blobWrite.Transfer(sourceData, "Base"); + TestData(*reinterpret_cast<SampleData*> (data.begin())); + + // Generate blob with reduce copy + BlobWrite::container_type dataReduced; + BlobWrite blobWriteReduce (dataReduced, kNoTransferInstructionFlags, kBuildNoTargetPlatform); + blobWriteReduce.SetReduceCopy(true); + blobWriteReduce.Transfer(sourceData, "Base"); + TestData(*reinterpret_cast<SampleData*> (dataReduced.begin())); + + // Ensure reduced blob is actually smaller. + CHECK (dataReduced.size() < data.size()); + + // Ensure that 64 bit data is larger than non-64 bit data + BlobWrite::container_type data64; + BlobWrite blobWrite64 (data64, kNoTransferInstructionFlags, kBuildStandaloneWin64Player); + blobWrite64.Transfer(sourceData, "Base"); + + BlobWrite::container_type data32; + BlobWrite blobWrite32 (data32, kNoTransferInstructionFlags, kBuildStandaloneWinPlayer); + blobWrite32.Transfer(sourceData, "Base"); + CHECK (data64.size() > data32.size()); + + DeleteTestData (sourceData); + } + + TEST (Blobification_OffsetPtr) + { + OffsetPtr<size_t>* ptrHigh = new OffsetPtr<size_t>; + OffsetPtr<size_t>* ptrLow = new OffsetPtr<size_t>; + + size_t* ptrH = reinterpret_cast<size_t*>(std::numeric_limits<size_t>::max()-4); + size_t* ptrL = reinterpret_cast<size_t*>(4); + + ptrHigh->reset(ptrH); + ptrLow->reset(ptrL); + + size_t h = reinterpret_cast<size_t>(ptrHigh->Get()); + size_t l = reinterpret_cast<size_t>(ptrLow->Get()); + + + CHECK (h == std::numeric_limits<size_t>::max()-4); + CHECK (l == 4); + + delete ptrHigh; + delete ptrLow; + } +} + +#endif //ENABLE_UNIT_TESTS diff --git a/Runtime/Serialize/Blobification/BlobWrite.cpp b/Runtime/Serialize/Blobification/BlobWrite.cpp new file mode 100644 index 0000000..dcb7705 --- /dev/null +++ b/Runtime/Serialize/Blobification/BlobWrite.cpp @@ -0,0 +1,148 @@ +#include "UnityPrefix.h" +#include "BlobWrite.h" +#include "Configuration/UnityConfigure.h" +#include "BlobWriteTargetSupport.h" + +BlobWrite::BlobWrite (container_type& blob, TransferInstructionFlags flags, BuildTargetPlatform targetPlatform) +: m_Blob(blob), + m_CopyData(true), + m_ReduceCopy(false), + m_TargetPlatform(targetPlatform) +{ + m_Flags = false; + m_SwapEndianess = m_Flags & kSwapEndianess; + m_Use64BitOffsetPtr = IsBuildTarget64BitBlob (targetPlatform); +} + +void BlobWrite::Push (size_t size, void* srcDataPtr, size_t align) +{ + Assert (m_CopyData); + + size_t offset = AlignAddress(m_Blob.size(), align); + m_Context.push( TypeContext(offset, 0, reinterpret_cast<UInt8*> (srcDataPtr), size) ); + m_Blob.resize_initialized(offset + size, 0); + m_CopyData = false; +} + +void BlobWrite::WritePtrValueAtLocation (size_t locationInBlob, SInt64 value) +{ + if (m_Use64BitOffsetPtr) + { + SInt64 offset64 = value; + + if (m_SwapEndianess) + SwapEndianBytes(offset64); + memcpy (&m_Blob[locationInBlob], &offset64, sizeof(offset64)); + } + else + { + SInt32 offset32 = value; + + if (m_SwapEndianess) + SwapEndianBytes(offset32); + memcpy (&m_Blob[locationInBlob], &offset32, sizeof(offset32)); + } +} + + +void BlobWrite::TransferPtrImpl (bool isValidPtr, ReduceCopyData* reduceCopyData, size_t alignOfT) +{ + Assert(!m_CopyData); + // When the data is null we will not call Transfer. + m_CopyData = isValidPtr; + + // Need to update OffsetPtr's member 'mOffset' + // compute member offset in memory buffer + size_t dataPosition = AlignAddress(m_Blob.size(), alignOfT); + size_t offset = GetActiveOffset(); + offset = dataPosition - offset; + if (!isValidPtr) + offset = 0; + + // Write the ptr + WritePtrValueAtLocation(GetActiveOffset (), offset); + + // Setup reduce copy data for later use by ReduceCopyImpl + if (reduceCopyData != NULL) + { + if (isValidPtr) + { + reduceCopyData->ptrPosition = GetActiveOffset(); + reduceCopyData->dataStart = dataPosition; + reduceCopyData->blobSize = m_Blob.size(); + } + else + { + reduceCopyData->ptrPosition = 0xFFFFF; + reduceCopyData->dataStart = 0xFFFFF; + reduceCopyData->blobSize = 0xFFFFF; + } + } + + + // Offset write location in the blob + m_Context.top().m_Offset += m_Use64BitOffsetPtr ? sizeof(SInt64) : sizeof(SInt32); + if (HasOffsetPtrWithDebugPtr()) + m_Context.top().m_Offset += sizeof(void*); +} + +bool BlobWrite::HasOffsetPtrWithDebugPtr () const +{ + return m_TargetPlatform == kBuildNoTargetPlatform; +} + +bool BlobWrite::AllowDataLayoutValidation () const +{ + size_t targetOffsetPtrSize = Use64BitOffsetPtr () ? sizeof(SInt64) : sizeof(SInt32); + if (HasOffsetPtrWithDebugPtr ()) + targetOffsetPtrSize += sizeof(void*); + + size_t srcOffsetPtrSize = sizeof(OffsetPtr<UInt8>); + + return targetOffsetPtrSize == srcOffsetPtrSize; +} + +// Ensure that the user has matching Transfer calls & Data layout in the struct +void BlobWrite::ValidateSerializedLayout (void* srcData, const char* name) +{ + UInt8* srcDataPtr = reinterpret_cast<UInt8*> (srcData); + + // (float4 and some others transfer functions, transfer temporary data, we ignore layout checks on those and hope for the best) + int srcDataOffset = srcDataPtr - m_Context.top().m_SourceDataPtr; + if (srcDataOffset < 0 || srcDataOffset >= m_Context.top().m_SourceDataSize) + return; + + // When targeting a platform with a different layout than our own, obviously these checks dont make sense... + if (!AllowDataLayoutValidation ()) + return; + + int blobOffset = m_Context.top().m_Offset; + if (srcDataOffset != blobOffset) + { + AssertString(Format("BlobWrite: Transfer '%s' is not called in the same order as the struct is laid out. Expected: %d got: %d ", name, srcDataOffset, blobOffset)); + } +} + +void BlobWrite::ReduceCopyImpl (const ReduceCopyData& reduce, size_t alignOfT) +{ + if (!m_ReduceCopy || reduce.dataStart == 0xFFFFF) + return; + + // Find any data in the blob that matches the last written data. + // if we find it, delete it again and reference the previous memory block instead + size_t dataSize = m_Blob.size() - reduce.dataStart; + for (int i=0;i < reduce.dataStart;i+=alignOfT) + { + if (memcmp(&m_Blob[i], &m_Blob[reduce.dataStart], dataSize) == 0) + { + // Update offset pointer + SInt64 offset = i - reduce.ptrPosition; + WritePtrValueAtLocation (reduce.ptrPosition, offset); + + // resize blob based on the reduce copy + m_Blob.resize_initialized(reduce.blobSize, 0); + + return; + } + } +} diff --git a/Runtime/Serialize/Blobification/BlobWrite.h b/Runtime/Serialize/Blobification/BlobWrite.h new file mode 100644 index 0000000..56a0340 --- /dev/null +++ b/Runtime/Serialize/Blobification/BlobWrite.h @@ -0,0 +1,220 @@ +#ifndef BLOBWRITE_H +#define BLOBWRITE_H + +#include "Runtime/Serialize/TransferFunctions/TransferBase.h" +#include "Runtime/Serialize/SwapEndianBytes.h" +#include "Runtime/Animation/MecanimArraySerialization.h" +#include "offsetptr.h" +#include "BlobSize.h" +#include "ReduceCopyData.h" +#include <stack> +#include "Runtime/Modules/ExportModules.h" + +class EXPORT_COREMODULE BlobWrite : public TransferBase +{ +public: + + typedef dynamic_array<UInt8, 16> container_type; + +private: + + container_type& m_Blob; + int m_TargetPlatform; + bool m_CopyData; + bool m_ReduceCopy; + bool m_Use64BitOffsetPtr; + bool m_SwapEndianess; + + struct TypeContext + { + TypeContext(size_t root, size_t offset, UInt8* srcDataPtr,size_t srcDataSize):m_Root(root),m_Offset(offset),m_SourceDataPtr(srcDataPtr),m_SourceDataSize(srcDataSize) {} + + size_t m_Root; + size_t m_Offset; + + UInt8* m_SourceDataPtr; + size_t m_SourceDataSize; + }; + std::stack<TypeContext> m_Context; + + std::size_t AlignAddress(std::size_t addr, std::size_t align) + { + return addr + ((~addr + 1U) & (align - 1U)); + } + + size_t GetActiveOffset () const + { + return m_Context.top().m_Root + m_Context.top().m_Offset; + } + + UInt8* GetActiveBlobPtr () + { + return &m_Blob[GetActiveOffset ()]; + } + + void WritePtrValueAtLocation (size_t locationInBlob, SInt64 value); + + void ValidateSerializedLayout (void* srcData, const char* name); + + void Push (size_t size, void* srcDataPtr, size_t align); + + void TransferPtrImpl (bool isValidPtr, ReduceCopyData* reduceCopyData, size_t alignOfT); + void ReduceCopyImpl (const ReduceCopyData& reduce, size_t alignOfT); + + bool HasOffsetPtrWithDebugPtr () const; + bool Use64BitOffsetPtr() const { return m_Use64BitOffsetPtr; } + bool AllowDataLayoutValidation () const; + +public: + + BlobWrite (container_type& blob, TransferInstructionFlags flags, BuildTargetPlatform targetPlatform); + + void SetReduceCopy (bool reduce) { m_ReduceCopy = reduce; } + + bool IsWriting () { return true; } + bool IsWritingPPtr () { return true; } + bool NeedsInstanceIDRemapping () { return m_Flags & kNeedsInstanceIDRemapping; } + bool ConvertEndianess () { return m_SwapEndianess; } + bool IsWritingGameReleaseData () + { + return true; + } + bool IsSerializingForGameRelease () + { + return true; + } + bool IsBuildingTargetPlatform (BuildTargetPlatform platform) + { + if (platform == kBuildAnyPlayerData) + return m_TargetPlatform >= kBuildValidPlayer; + else + return m_TargetPlatform == platform; + } + + BuildTargetPlatform GetBuildingTargetPlatform () { return static_cast<BuildTargetPlatform>(m_TargetPlatform); } + + template<class T> + void Transfer (T& data, const char* name, TransferMetaFlags metaFlag = kNoTransferFlags); + + template<class T> + void TransferWithTypeString (T& data, const char* name, const char* typeName, TransferMetaFlags metaFlag = kNoTransferFlags); + + template<class T> + void TransferBasicData (T& data); + + template<class T> + void TransferPtr (bool isValidPtr, ReduceCopyData* reduceCopyData); + + template<class T> + void ReduceCopy (const ReduceCopyData& reduce); + + template<class T> + void TransferSTLStyleArray (T& data, TransferMetaFlags metaFlag = kNoTransferFlags); + + template<class> friend class BlobWriteTransferSTLStyleArrayImpl; +}; + +template<typename T> class BlobWriteTransferSTLStyleArrayImpl +{ +public: + void operator()(T& data, TransferMetaFlags metaFlags, BlobWrite& transfer) + { + AssertString ("STL array are not support for BlobWrite"); + } +}; + +template<typename T> class BlobWriteTransferSTLStyleArrayImpl< OffsetPtrArrayTransfer<T> > +{ +public: + void operator()(OffsetPtrArrayTransfer<T>& data, TransferMetaFlags metaFlags, BlobWrite& transfer) + { + if (data.size() == 0) + { + Assert (!transfer.m_CopyData); + return; + } + Assert (transfer.m_CopyData); + + size_t arrayByteSize = BlobSize::CalculateSize(*data.begin(), transfer.HasOffsetPtrWithDebugPtr(), transfer.Use64BitOffsetPtr()) * data.size(); + transfer.Push(arrayByteSize, &*data.begin(), ALIGN_OF( typename OffsetPtrArrayTransfer<T>::value_type )); + + typename OffsetPtrArrayTransfer<T>::iterator end = data.end (); + for (typename OffsetPtrArrayTransfer<T>::iterator i = data.begin ();i != end;++i) + transfer.Transfer (*i, "data"); + + transfer.m_Context.pop(); + } +}; + +template<typename T, int SIZE> class BlobWriteTransferSTLStyleArrayImpl< StaticArrayTransfer<T, SIZE> > +{ +public: + void operator()(StaticArrayTransfer<T, SIZE>& data, TransferMetaFlags metaFlags, BlobWrite& transfer) + { + typename StaticArrayTransfer<T, SIZE>::iterator end = data.end (); + for (typename StaticArrayTransfer<T, SIZE>::iterator i = data.begin ();i != end;++i) + transfer.Transfer (*i, "data"); + } +}; + +template<class T> inline +void BlobWrite::TransferSTLStyleArray (T& data, TransferMetaFlags metaFlags) +{ + BlobWriteTransferSTLStyleArrayImpl<T> transfer; + transfer(data, metaFlags, *this); +} + +template<class T> inline +void BlobWrite::Transfer (T& data, const char* name, TransferMetaFlags) +{ + bool copyData = m_CopyData; + if (m_CopyData) + Push(BlobSize::CalculateSize(data, HasOffsetPtrWithDebugPtr(), Use64BitOffsetPtr()), &data, SerializeTraits<T>::GetAlignOf() ); + + // Follow natural c++ alignment + size_t head = m_Context.top().m_Root; + size_t& offset = m_Context.top().m_Offset; + // always align head + offset not only offset otherwise you may get wrong align for nested data structure + offset = AlignAddress(head + offset, SerializeTraits<T>::GetAlignOf()) - head; + + ValidateSerializedLayout(&data, name); + + SerializeTraits<T>::Transfer (data, *this); + + if (copyData) + m_Context.pop(); +} + +template<class T> inline +void BlobWrite::TransferWithTypeString (T& data, const char*, const char*, TransferMetaFlags) +{ + SerializeTraits<T>::Transfer (data, *this); +} + +template<class T> inline +void BlobWrite::TransferBasicData (T& srcData) +{ + Assert(m_Blob.size() >= GetActiveOffset() + sizeof(T)); + + // Write basic data into blob & endianswap + UInt8* blobPtr = GetActiveBlobPtr(); + memcpy(blobPtr, &srcData, sizeof(T)); + if (m_SwapEndianess) + SwapEndianBytes(*reinterpret_cast<T*> (blobPtr)); + + m_Context.top().m_Offset += sizeof(T); +} + +template<class T> inline +void BlobWrite::TransferPtr (bool isValidPtr, ReduceCopyData* reduceCopyData) +{ + TransferPtrImpl (isValidPtr, reduceCopyData, ALIGN_OF(T)); +} + +template<class T> inline +void BlobWrite::ReduceCopy (const ReduceCopyData& reduce) +{ + ReduceCopyImpl(reduce, ALIGN_OF(T)); +} + +#endif diff --git a/Runtime/Serialize/Blobification/BlobWriteTargetSupport.cpp b/Runtime/Serialize/Blobification/BlobWriteTargetSupport.cpp new file mode 100644 index 0000000..b66b941 --- /dev/null +++ b/Runtime/Serialize/Blobification/BlobWriteTargetSupport.cpp @@ -0,0 +1,31 @@ +#include "UnityPrefix.h" +#include "BlobWriteTargetSupport.h" + +bool DoesBuildTargetSupportBlobification (BuildTargetPlatform target, TransferInstructionFlags flags) +{ + // If we are writing typetrees, then we can't use blobification + bool writeTypeTree = (flags & kDisableWriteTypeTree) == 0; + if (writeTypeTree) + return false; + + + // Webplayer & Editor should never use blobification + Assert(target != kBuildWebPlayerLZMA && target != kBuildWebPlayerLZMAStreamed && target != kBuildAnyPlayerData || target == kBuildNoTargetPlatform); + return true; +} + +bool IsBuildTarget64BitBlob (BuildTargetPlatform target) +{ + Assert(target != kBuildAnyPlayerData && target != kBuildWebPlayerLZMA && target != kBuildWebPlayerLZMAStreamed); + + // Building blob for the editor (Choose whatever we are running with) + if (target == kBuildNoTargetPlatform) + return sizeof(size_t) == sizeof(UInt64); + + // Known 64 bit platform? + bool target64Bit = target == kBuildMetroPlayerX64 || target == kBuildStandaloneWin64Player || target == kBuildStandaloneLinux64 || target == kBuildStandaloneLinuxUniversal; + if (target64Bit) + return true; + + return false; +}
\ No newline at end of file diff --git a/Runtime/Serialize/Blobification/BlobWriteTargetSupport.h b/Runtime/Serialize/Blobification/BlobWriteTargetSupport.h new file mode 100644 index 0000000..76191f5 --- /dev/null +++ b/Runtime/Serialize/Blobification/BlobWriteTargetSupport.h @@ -0,0 +1,6 @@ +#pragma once + +#include "Runtime/Serialize/SerializationMetaFlags.h" + +bool DoesBuildTargetSupportBlobification (BuildTargetPlatform target, TransferInstructionFlags flags); +bool IsBuildTarget64BitBlob (BuildTargetPlatform target);
\ No newline at end of file diff --git a/Runtime/Serialize/Blobification/OffsetPtrTest.cpp b/Runtime/Serialize/Blobification/OffsetPtrTest.cpp new file mode 100644 index 0000000..9ddd2a8 --- /dev/null +++ b/Runtime/Serialize/Blobification/OffsetPtrTest.cpp @@ -0,0 +1,31 @@ +#include "UnityPrefix.h" + +#include "Runtime/Serialize/SerializeTraits.h" +#include "offsetptr.h" + + +#include <limits> + + +void TestOffsetPtr () +{ + OffsetPtr<size_t>* ptrHigh = new OffsetPtr<size_t>; + OffsetPtr<size_t>* ptrLow = new OffsetPtr<size_t>; + + size_t* ptrH = reinterpret_cast<size_t*>(std::numeric_limits<size_t>::max()-4); + size_t* ptrL = reinterpret_cast<size_t*>(4); + + ptrHigh->reset(ptrH); + ptrLow->reset(ptrL); + + size_t h = reinterpret_cast<size_t>(ptrHigh->Get()); + size_t l = reinterpret_cast<size_t>(ptrLow->Get()); + + + Assert(h == std::numeric_limits<size_t>::max()-4); + Assert(l == 4); + + delete ptrHigh; + delete ptrLow; + +} diff --git a/Runtime/Serialize/Blobification/ReduceCopyData.h b/Runtime/Serialize/Blobification/ReduceCopyData.h new file mode 100644 index 0000000..ae4952f --- /dev/null +++ b/Runtime/Serialize/Blobification/ReduceCopyData.h @@ -0,0 +1,8 @@ +#pragma once + +struct ReduceCopyData +{ + size_t ptrPosition; + size_t dataStart; + size_t blobSize; +}; diff --git a/Runtime/Serialize/Blobification/offsetptr.h b/Runtime/Serialize/Blobification/offsetptr.h new file mode 100644 index 0000000..7ef1809 --- /dev/null +++ b/Runtime/Serialize/Blobification/offsetptr.h @@ -0,0 +1,257 @@ +#pragma once + +#include "Runtime/mecanim/memory.h" +#include "Runtime/Serialize/SerializeTraits.h" +#include "Runtime/Serialize/SerializeUtility.h" +#include "Runtime/Serialize/SerializeTraitsBase.h" +#include "Runtime/Serialize/TransferFunctionFwd.h" +#include "Runtime/Utilities/TypeUtilities.h" +#include "ReduceCopyData.h" + +template<typename TYPE> +class OffsetPtr +{ +public: + typedef TYPE value_type; + typedef TYPE* ptr_type; + typedef TYPE const* const_ptr_type; + typedef TYPE& reference_type; + typedef TYPE const& const_reference_type; + typedef size_t offset_type; + + OffsetPtr():m_Offset(0),m_DebugPtr(0) + { + } + + OffsetPtr (const OffsetPtr<value_type>& ptr):m_Offset(ptr.m_Offset) + { + } + + OffsetPtr& operator = (const OffsetPtr<value_type>& ptr) + { + m_Offset = ptr.m_Offset; + return *this; + } + + void reset(ptr_type ptr) + { + m_Offset = ptr != 0 ? reinterpret_cast<size_t>(ptr) - reinterpret_cast<size_t>(this) : 0; +#ifdef UNITY_EDITOR + m_DebugPtr = ptr; +#endif + } + + OffsetPtr& operator = (const ptr_type ptr) + { + reset (ptr); + return *this; + } + + ptr_type operator->() + { + ptr_type ptr = reinterpret_cast<ptr_type>(reinterpret_cast<std::size_t>(this) + m_Offset); +#ifdef UNITY_EDITOR + m_DebugPtr = ptr; +#endif + return ptr; + } + const_ptr_type operator->()const + { + return reinterpret_cast<const_ptr_type>(reinterpret_cast<std::size_t>(this) + m_Offset); + } + + reference_type operator*() + { + ptr_type ptr = reinterpret_cast<ptr_type>(reinterpret_cast<std::size_t>(this) + m_Offset); +#ifdef UNITY_EDITOR + m_DebugPtr = ptr; +#endif + return *ptr; + } + + const_reference_type operator*()const + { + return *reinterpret_cast<const_ptr_type>(reinterpret_cast<std::size_t>(this) + m_Offset); + } + + value_type& operator[](std::size_t i ) + { + assert(i != std::numeric_limits<std::size_t>::max()); + ptr_type ptr = reinterpret_cast<ptr_type>(reinterpret_cast<std::size_t>(this) + m_Offset); +#ifdef UNITY_EDITOR + m_DebugPtr = ptr; +#endif + return ptr[i]; + } + + value_type const& operator[](std::size_t i ) const + { + assert(i != std::numeric_limits<std::size_t>::max()); + const_ptr_type ptr = reinterpret_cast<const_ptr_type>(reinterpret_cast<std::size_t>(this) + m_Offset); + return ptr[i]; + } + + bool IsNull()const + { + return m_Offset == 0; + } + + ptr_type Get() + { +#ifdef UNITY_EDITOR + m_DebugPtr = reinterpret_cast<ptr_type>(reinterpret_cast<std::size_t>(this) + m_Offset); +#endif + // TODO: serialize trait for offset ptr call begin and end which call OffsetPtr::Get + //Assert(!IsNull()); + return reinterpret_cast<ptr_type>(reinterpret_cast<std::size_t>(this) + m_Offset); + } + + const_ptr_type Get()const + { +#ifdef UNITY_EDITOR + m_DebugPtr = reinterpret_cast<ptr_type>(reinterpret_cast<std::size_t>(this) + m_Offset); +#endif + // TODO: serialize trait for offset ptr call begin and end which call OffsetPtr::Get + //Assert(!IsNull()); + return reinterpret_cast<ptr_type>(reinterpret_cast<std::size_t>(this) + m_Offset); + } + + + size_t get_size () const + { + return sizeof(TYPE); + } + +protected: + offset_type m_Offset; +#ifdef UNITY_EDITOR + mutable ptr_type m_DebugPtr; +#endif +}; + + +template<typename TYPE> +class SerializeTraits< OffsetPtr<TYPE> > : public SerializeTraitsBase< OffsetPtr<TYPE> > +{ + public: + + typedef OffsetPtr<TYPE> value_type; + inline static const char* GetTypeString (void*) { return "OffsetPtr"; } + inline static bool IsAnimationChannel () { return false; } + inline static bool MightContainPPtr () { return true; } + inline static bool AllowTransferOptimization () { return false; } + + template<class TransferFunction> inline + static void Transfer (value_type& data, TransferFunction& transfer) + { + if(IsSameType<TransferFunction, BlobWrite>::result) + { + ReduceCopyData reduce; + transfer.template TransferPtr<TYPE>(!data.IsNull(), &reduce); + if (!data.IsNull()) + { + transfer.Transfer(*data, "data"); + } + transfer.template ReduceCopy<TYPE> (reduce); + } + else if(transfer.IsReading () || transfer.IsWriting ()) + { + bool isNull = data.IsNull(); + + transfer.template TransferPtr<TYPE>(true, NULL); + if (isNull) + { + mecanim::memory::ChainedAllocator* allocator = static_cast<mecanim::memory::ChainedAllocator*> (transfer.GetUserData()); + data = allocator->Construct<TYPE>(); + } + + transfer.Transfer(*data, "data"); + } + else if(IsSameType<TransferFunction, BlobSize>::result) + { + transfer.template TransferPtr<TYPE>(false, NULL); + } + // Support for ProxyTransfer + else + { + transfer.template TransferPtr<TYPE>(false, NULL); + + TYPE p; + transfer.Transfer(p, "data"); + } + } +}; + +template<class T> +struct OffsetPtrArrayTransfer +{ + typedef T* iterator; + typedef T value_type; + + OffsetPtr<T>& m_Data; + UInt32& m_ArraySize; + void* m_Allocator; + bool m_ClearPtrs; + + OffsetPtrArrayTransfer (OffsetPtr<T>& data, UInt32& size, void* allocator, bool clearPtrs) + : m_Data(data),m_ArraySize(size) + { + m_Allocator = allocator; + m_ClearPtrs = clearPtrs; + } + + T* begin () { return m_Data.Get(); } + T* end () { return m_Data.Get() + m_ArraySize; } + size_t size() { return m_ArraySize; } + + void resize (int newSize) + { + m_ArraySize = newSize; + + mecanim::memory::ChainedAllocator* allocator = static_cast<mecanim::memory::ChainedAllocator*> (m_Allocator); + Assert(allocator != NULL); + + if (newSize != 0) + { + m_Data = allocator->ConstructArray<value_type> (newSize); + if (m_ClearPtrs) + memset(begin(), 0, sizeof(value_type) * newSize); + } + else + m_Data = NULL; + } +}; + +template<class T> +class SerializeTraits<OffsetPtrArrayTransfer<T> > : public SerializeTraitsBase<OffsetPtrArrayTransfer<T> > +{ +public: + + typedef OffsetPtrArrayTransfer<T> value_type; + DEFINE_GET_TYPESTRING_CONTAINER (vector) + + template<class TransferFunction> inline + static void Transfer (value_type& data, TransferFunction& transfer) + { + ReduceCopyData reduceCopy; + transfer.template TransferPtr<typename value_type::value_type>(transfer.IsReading() || transfer.IsWriting() ? data.m_ArraySize != 0 : false, &reduceCopy); + + transfer.TransferSTLStyleArray (data); + transfer.template ReduceCopy<typename value_type::value_type>(reduceCopy); + } + + static bool IsContinousMemoryArray () { return true; } + static void ResizeSTLStyleArray (value_type& data, int rs) + { + data.resize(rs); + } + + static void resource_image_assign_external (value_type& data, void* begin, void* end) + { + } +}; + + +#define MANUAL_ARRAY_TRANSFER2(TYPE,DATA,SIZE) OffsetPtrArrayTransfer<TYPE> DATA##ArrayTransfer (DATA, SIZE, transfer.GetUserData(), false); transfer.Transfer(DATA##ArrayTransfer, #DATA); +#define TRANSFER_BLOB_ONLY(DATA) if (IsSameType<TransferFunction, BlobWrite>::result || IsSameType<TransferFunction, BlobSize>::result) transfer.Transfer(DATA, #DATA); + |