summaryrefslogtreecommitdiff
path: root/Runtime/Serialize/Blobification
diff options
context:
space:
mode:
authorchai <chaifix@163.com>2019-08-14 22:50:43 +0800
committerchai <chaifix@163.com>2019-08-14 22:50:43 +0800
commit15740faf9fe9fe4be08965098bbf2947e096aeeb (patch)
treea730ec236656cc8cab5b13f088adfaed6bb218fb /Runtime/Serialize/Blobification
+Unity Runtime codeHEADmaster
Diffstat (limited to 'Runtime/Serialize/Blobification')
-rw-r--r--Runtime/Serialize/Blobification/BlobSize.h161
-rw-r--r--Runtime/Serialize/Blobification/BlobTests.cpp358
-rw-r--r--Runtime/Serialize/Blobification/BlobWrite.cpp148
-rw-r--r--Runtime/Serialize/Blobification/BlobWrite.h220
-rw-r--r--Runtime/Serialize/Blobification/BlobWriteTargetSupport.cpp31
-rw-r--r--Runtime/Serialize/Blobification/BlobWriteTargetSupport.h6
-rw-r--r--Runtime/Serialize/Blobification/OffsetPtrTest.cpp31
-rw-r--r--Runtime/Serialize/Blobification/ReduceCopyData.h8
-rw-r--r--Runtime/Serialize/Blobification/offsetptr.h257
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);
+