Hello Everyone I am working on a small mobile experience in which the core experience is recording and replaying an objects movements and interactions within a single scene. While everything is working well for the most part I am having a really hard time de-allocating and re-allocating memory for large arrays and data objects we use to hold the audio and recorded object data over time .
Basically once we save roughly 1 minute of recorded data ( audio , various transforms on various objects) we get about 10 - 12 Mb’s of data and store it to the memory of the mobile device . We take all of this data , grabbing the various " Trackers" we use that record /store the object transforms , and create a float array of audio data for our accompanying audio clip .
During the process of actually saving this data we convert the object Transform data trackers to JSON and store the data in player prefs and then we write the audio bytes to memory ( I will share all my code for this at the bottom) . It seems like somewhere I am causing a memory " Leak " where some of the data is still being retained and after roughly 6 save processes the games frame rate nose dives to the point of essentially pausing the game .
Does anyone who has saved large data structures or a large amount of memory have an idea of what I may be doing wrong or certain memory considerations I may be overlooking that Unity has ?
My save process is mainly broken down into 3 Separate functions , One for storing and saving audio data, one for storing the object data in player prefs, and one last area where we save various save details to an overall data manager .
Here is my code for converting the object to Json and saving to player prefs ( the area I least likely to expect the memory issues to come from )
[FONT=Courier New]//*** Writing a function to save and eventually load our data slots indepenedently of eachother so that we can load lal the data into memory as quickly as possible
public void SaveDataSlot( int saveDataSlot){
//*** First we convert the playback data to Json and serialize the data
string xData = JsonHelper.Serialize(PlayGlobal.level.PlaybackObject);
//*** We then use the encryption algorithm to encrypt the data
string xEncryptedData = SecurityHelper.AES_Encrypt(xData, AES_KEY);
//*** We use the Hash to give us a code to decrypt the existing data
string xHash = xEncryptedData.Substring(HASH_START, HASH_LENGTH);
xHash = xHash.Length < HASH_LENGTH ? xHash.PadRight(HASH_LENGTH - xHash.Length, '*') : xHash;
xHash = SecurityHelper.Md5Sum(xHash + HASH_ADD);
//*** Delete Pre-existing save data
if(PlayerPrefs.HasKey(saveSlotName+saveDataSlot.ToString())){
PlayerPrefs.DeleteKey(saveSlotName+saveDataSlot.ToString());
PlayerPrefs.Save();
}
// we save our playerPrefs
//*** Set Data
PlayerPrefs.SetString(saveSlotName+saveDataSlot.ToString(), xEncryptedData);
//PlayerPrefs.SetString(PLAYERPREFS_HASH, xHash);
// we save our playerPrefs
PlayerPrefs.Save();
//PlayerPrefs.SetString(saveSlotName+saveDataSlot, "slotstring");
//*** unload unused assets......
Resources.UnloadUnusedAssets();
}[/FONT]
Here IS my code for saving the Audio Data
[FONT=Courier New]//*** set playback sample count based on our sample rate of 11025 samples * clip length
playbackLayer.audioSamples = Mathf.CeilToInt(numOfSamples * playbackLayer.time);
//*** create a float array the length of the number of audio float samples we get back
saveAudioFloatData = new float[playbackLayer.audioSamples];
//*** here we get the audio data form the current playback and begin playing the clip
PlayGlobal.recorder.layers[L].clip.GetData(saveAudioFloatData,0);
//DebugTools.Log("Saving Data!!!! audioData.length = " + audioFloatData.Length.ToString());
//**** Begin setting up audio clip name and setting the filepath
audioClipName ="Slot"+pslot.ToString()+ "clip" + L.ToString();
filePath = "";
if(Application.platform == RuntimePlatform.OSXEditor || Application.platform == RuntimePlatform.OSXPlayer || Application.platform == RuntimePlatform.WindowsEditor || Application.platform == RuntimePlatform.WindowsPlayer)
{
//*** Here we create a file path based on combining our file name with our data path
filePath = Path.Combine(Application.dataPath ,audioClipName);
// if this file exists , we read the existing bytes
if(File.Exists(filePath)){
//*** we remove pre-existing data
System.IO.File.Delete(filePath);
}
//else if unity is currently on an iphone or android device , load bytes from the persistent data path
}else if(Application.platform == RuntimePlatform.IPhonePlayer || Application.platform == RuntimePlatform.Android){
//*** Here we create a file path based on combining our file name with our data path
filePath = Path.Combine(Application.persistentDataPath ,audioClipName);
// if this file exists , we read the existing bytes
if(File.Exists(filePath)){
//*** we remove pre-existing data
System.IO.File.Delete(filePath);
}
}
//yield return new WaitForEndOfFrame();
//*** Convert float data to Bytes
ConvertFloatsToBytes();
DebugTools.Log(" converting bytes to float ");
yield return new WaitForEndOfFrame();
System.IO.File.WriteAllBytes(filePath,saveAudioByteData);
DebugTools.Log(" Saved bytes ");
yield return new WaitForEndOfFrame();
//*** free memory allocated for audio float data array
saveAudioByteData = null;
saveAudioByteData = new byte[0];[/FONT]
and here is my code specifically for converting Byte Data To Float Data
[FONT=Courier New]public void ConvertFloatsToBytes()
{
//*** create a byte array 4 times the size of the float array to hold the value for each float in bytes
saveAudioByteData = new byte[saveAudioFloatData.Length * 4];
//*** This function converts our current float array elements to the same exact place in byte data
Buffer.BlockCopy(saveAudioFloatData,0,saveAudioByteData,0,saveAudioByteData.Length);
//*** Release audio float data array from memory
saveAudioFloatData = null;
saveAudioFloatData = new float[0];
}[/FONT]
The last section is devoted to us populating our tracked transform data which is basically this particular snippet repeated and modified based on the tracked object in question .
[FONT=Courier New] //*** We populate all of the tracker data for our given layer
for( i = 0 ; i < PlayGlobal.recorder.puppetTrackers[puppetIndex].trackers_TransformData.Count ; i++)
{
//*** Here we add our transform data to our current record data stack
PlayRecordData_Transform convertedTransformData = new PlayRecordData_Transform();
//*** Here we place the converted transform record data
convertedTransformData = PlayGlobal.recorder.puppetTrackers[puppetIndex].trackers_TransformData[i] as PlayRecordData_Transform;
//*** probably unnecessary but just making sure the converted transform data exists
if (convertedTransformData !=null)
{
//*** and then we add the new transform tracker data to our tracker data list
playbackLayer.puppetRecordTrackers[puppetNumInLayer].trackers_TransformData.Add(convertedTransformData);
convertedTransformData = null;
}
}//*** End Transform Data Population[/FONT]
I thank you for any and all responses, I am not certain if the issue is merely me overlooking something in my code or if maybe some portion of my process is gradually wearing down my available memory over time and causing a massive performance drop .
The issue is a little more confusing because on a particular Android Device we are able to save roughly 11 times with a very limited performance drop while on all the iOS and most mobile Android devices the " 6 saves before meltdown " is very consistent .
I suspect that considering we are writing byte arrays with hundreds of thousands of elements and my individual save trackers can have upwards of 8,000 data members in their lists somewhere I am not freeing up th extraordinary amount of memory our save process uses .