- Home /
Background downloading causing performance jitters
Hi Everyone
Apologies if this has been answered, but I've looked everywhere in the last week and could not find an answer to my exact problem.
I am using www to download levels that are stored in assetbundles (WWW.LoadFromCacheOrDownload). When the game starts the first level is downloaded using a GUI and a progress bar. While the player is busy playing this level I start downloading the next level so that it might be ready for him by the time he completes the first level. It works great, except for jitter during the download. (Not when the asset file is being loaded at the end of the download). If I set my local apache server SendBufferSize to something small like 512 the download jitter disappears. If I increase the SendBufferSize the jitter increases accordingly. This make me think that Unity downloads a chunk of data into memory and does some processing at the end of each buffered chunk. (Write it to disk?) The bigger the chunk of data the more the game play will experience hiccups.
Now while adjusting SendBufferSize sounds like a solution, it does not work when the assetbundle is hosted on a public server. If I adjust the SendBufferSize it makes no difference. I assume there are caching servers between my device and the server and I am unable to adjust SendBufferSize on those.
I have tried modifying the www thread priority as well as backgroundLoadingPriority but it caused only a slight improvement.
Platform: iOS Xcode 5.1.1 Unity 4.5.2p1
Any ideas?
www calls tend to halt things until they are complete. I'm not sure what best practise is but maybe it can be handled with a coroutine?
There is indeed a coroutine using a while(!download.isDone) loop.
Indeed, you can put the www handling inside a coroutine and yield while the download is not done. We use it where I work for downloading images on app startup and it ends up downloading like 40 images without fixed res and it handles it pretty well. On the other hand , seeing you rolled your own, might be better. Not really a fan of www class. Seems like patchwork.
Hi, if I put www in coroutine, downloading still causes jittering and freezes(each asset bundle about 3mb), I think it is because courutine works in main thread.
Answer by 3d_Game_Ready · Dec 08, 2014 at 10:33 AM
I had to implement a custom downloader since the Unity WWW jitter could not be resolved.
Hello, could you share you implementation of custom downloader?
I'm trying to post it but it tells me I can only post 3000 characters max.
using System.Text; using UnityEngine;
public class Downloader { uint contentLength; int bytesDownloaded = 0; int read = 0; public float progress = 0.0f;
NetworkStream networkStream;
static FileStream fileStream;
Socket client;
public string error = null;
public Downloader (string uRL, string filename)
{
Uri myUri = new Uri(uRL);
string host = myUri.Host;
//UnityEngine.Debug.Log ("host:" + host);
string uri = uRL.Substring (uRL.IndexOf(host)+host.Length);
//UnityEngine.Debug.Log ("uri:" + uri);
string query = "GET " + uri.Replace(" ", "%20") + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"User-Agent: undefined\r\n" +
"Connection: close\r\n"+
"\r\n";
//UnityEngine.Debug.Log (query);
client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
try{
client.Connect(host, 80);
networkStream = new NetworkStream(client);
var bytes = Encoding.Default.GetBytes(query);
networkStream.Write(bytes, 0, bytes.Length);
}
catch(Exception e){
UnityEngine.Debug.LogError ("Connection Error:" + e.$$anonymous$$essage);
error = "Are you connected to the internet?";
return;
}
var bReader = new BinaryReader(networkStream, Encoding.Default);
string response = "";
string line;
char c;
do
{
line = "";
c = '\u0000';
while (true)
{
c = bReader.ReadChar();
if (c == '\r')
break;
line += c;
}
c = bReader.ReadChar();
response += line + "\r\n";
}
while (line.Length > 0);
//UnityEngine.Debug.Log ( response );
Regex reContentLength = new Regex(@"(?<=Content-Length:\s)\d+", RegexOptions.IgnoreCase);
contentLength = uint.Parse(reContentLength.$$anonymous$$atch(response).Value);
try{
string fileName = UnityEngine.Application.persistentDataPath + "/" + filename;
if(System.IO.File.Exists(fileName)){
System.IO.File.Delete(fileName);
}
fileStream = new FileStream( fileName, File$$anonymous$$ode.Create);
iPhone.SetNoBackupFlag (fileName);
}
catch(Exception e){
UnityEngine.Debug.LogError ("fileStream exception:" + e.$$anonymous$$essage);
error = e.$$anonymous$$essage;
if(fileStream != null){
fileStream.Flush();
fileStream.Close();
}
}
}
public bool GetNextBuffer(int kilobytes)
{
//try{
byte[] buffer = new byte[kilobytes * 1024];
if (bytesDownloaded < contentLength)
{
if (networkStream.DataAvailable)
{
read = networkStream.Read(buffer, 0, buffer.Length);
bytesDownloaded += read;
try{
fileStream.Write(buffer, 0, read);
}
catch(Exception e){
UnityEngine.Debug.Log("Exception while writing file:" + e.$$anonymous$$essage);
error = e.$$anonymous$$essage;
if(fileStream!=null){
fileStream.Flush();
fileStream.Close();
}
client.Close();
return true;
}
}
progress = (float)((float)bytesDownloaded/(float)contentLength);
//UnityEngine.Debug.Log ( "Downloaded: " + bytesDownloaded + " of " + contentLength + " bytes ..." );
return false;
}
else
{
fileStream.Flush();
fileStream.Close();
client.Close();
return true;
}
return true;
//}
}
}
Ok, was able to add it in two code segments. This was adapted from code I found online. The buffer size is adjustable.