Wednesday, October 9, 2019

UnityWebRequest avoid GC overhead

https://docs.unity3d.com/540/Documentation/Manual/UnityWebRequest.html

For more on preallocated data buffers, see the following subsection.
Avoiding GC Overhead: DownloadHandlerScripts with pre-allocated buffers
Many of Unity’s power users are constantly concerned with reducing CPU spikes due to garbage collection. For these users, the UnityWebRequest system permits the pre-allocation of a managed-code byte array which will be used to deliver downloaded data to DownloadHandlerScript’s ReceiveData callback.
Using this method completely eliminates managed-code memory allocation when using DownloadHandlerScript-derived classes to capture downloaded data.
To make a DownloadHandlerScript operate with a pre-allocated managed buffer, simply supply a byte array to the constructor of DownloadHandlerScript.
Note: The size of the byte array limits the amount of data delivered to the ReceiveData callback each frame. Do not provide too small of a byte array or your data will arrive slowly, over many frames.

https://stackoverflow.com/questions/49524324/obtain-data-from-unitywebrequest-while-still-downloading

It is possible to do it with UnityWebRequest but you have to perform extra work. The secret is to use DownloadHandlerScript with UnityWebRequest.
Create a separate class and make it derive from DownloadHandlerScript instead of MonoBehaviour. In the example below, I will call it CustomWebRequest. Override the bool ReceiveData(byte[] data, int dataLength)void CompleteContent() and the void ReceiveContentLength(int contentLength) functions inside it.
Here is an example of the CustomWebRequest class:

public class CustomWebRequest : DownloadHandlerScript
{
    // Standard scripted download handler - will allocate memory on each ReceiveData callback
    public CustomWebRequest()
        : base()
    {
    }

    // Pre-allocated scripted download handler
    // Will reuse the supplied byte array to deliver data.
    // Eliminates memory allocation.
    public CustomWebRequest(byte[] buffer)
        : base(buffer)
    {
    }

    // Required by DownloadHandler base class. Called when you address the 'bytes' property.
    protected override byte[] GetData() { return null; }

    // Called once per frame when data has been received from the network.
    protected override bool ReceiveData(byte[] byteFromServer, int dataLength)
    {
        if (byteFromServer == null || byteFromServer.Length < 1)
        {
            Debug.Log("CustomWebRequest :: ReceiveData - received a null/empty buffer");
            return false;
        }

        //Do something with or Process byteFromServer here


        return true;
    }

    // Called when all data has been received from the server and delivered via ReceiveData
    protected override void CompleteContent()
    {
        Debug.Log("CustomWebRequest :: CompleteContent - DOWNLOAD COMPLETE!");
    }

    // Called when a Content-Length header is received from the server.
    protected override void ReceiveContentLength(int contentLength)
    {
        Debug.Log(string.Format("CustomWebRequest :: ReceiveContentLength - length {0}", contentLength));
    }
}
And to use it to start the download, just create new instance of UnityWebRequest and use CustomWebRequest as the downloadHandler before calling the SendWebRequest function. No coroutine required for this and you don't even have to yield anything. This is how to do it:
UnityWebRequest webRequest;
//Pre-allocate memory so that this is not done each time data is received
byte[] bytes = new byte[2000];

void Start()
{
    string url = "http://yourUrl/mjpg/video.mjpg";
    webRequest = new UnityWebRequest(url);
    webRequest.downloadHandler = new CustomWebRequest(bytes);
    webRequest.SendWebRequest();
}
That's it.
  • When Unity first makes the request, ReceiveContentLength(int contentLength) is called. You can use the contentLength parameter to determine the total number of bytes you will receive from that request.
  • The ReceiveData function is called each time new data is received and from that function, you can access that current data from the byteFromServer parameter and the length of it from the dataLength parameter.
  • When Unity is done receiving data, the CompleteContent function is called.

No comments:

Post a Comment