Created
September 2, 2020 10:00
-
-
Save ericallam/2b53c5525e8a5e1645a7ad13495464fb to your computer and use it in GitHub Desktop.
UnityWebRequest DownloadTexture leak
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using MEC; | |
using RSG; | |
using UnityEngine; | |
using UnityEngine.Networking; | |
// This is the original implementation | |
namespace DefaultNamespace | |
{ | |
public class TextureFromFile | |
{ | |
private string FilePath { get; set; } | |
private UnityWebRequest _unityWebRequest; | |
private bool _isRunning; | |
public TextureFromFile(string filePath) | |
{ | |
FilePath = filePath; | |
_isRunning = false; | |
} | |
public static TextureFromFile AtPath(string filePath) | |
{ | |
return new TextureFromFile(filePath); | |
} | |
public IPromise<Texture2D> GetTexture2D(Func<bool> cancelEvaluator) { | |
if (FilePath == null) | |
{ | |
return Promise<Texture2D>.Rejected(new Exception("FilePath is empty")); | |
} | |
Promise<Texture2D> promise = new Promise<Texture2D>(); | |
Timing.RunCoroutine(TextureFromFilePathRoutine(promise).CancelWith(cancelEvaluator)); | |
return promise; | |
} | |
public IPromise<Texture2D> GetTexture2D(GameObject activeGameObject) { | |
if (FilePath == null) | |
{ | |
return Promise<Texture2D>.Rejected(new Exception("FilePath is empty")); | |
} | |
Promise<Texture2D> promise = new Promise<Texture2D>(); | |
Timing.RunCoroutine(TextureFromFilePathRoutine(promise).CancelWith(activeGameObject)); | |
return promise; | |
} | |
public IPromise<Texture2D> GetTexture2DWhileGameObjectExists(GameObject cancelGameObject) | |
{ | |
return GetTexture2D(() => cancelGameObject != null); | |
} | |
public IPromise<Texture2D> GetTexture2DWhileGameObjectIsActive(GameObject cancelGameObject) | |
{ | |
return GetTexture2D(() => cancelGameObject != null && cancelGameObject.activeInHierarchy); | |
} | |
public IPromise<Sprite> GetSprite(Func<bool> cancelEvaluator) | |
{ | |
Promise<Sprite> promise = new Promise<Sprite>(); | |
GetTexture2D(cancelEvaluator) | |
.Then(texture2D => | |
{ | |
Sprite sprite = Sprite.Create(texture2D, new Rect(0f, 0f, texture2D.width, texture2D.height), new Vector2(.5f, .5f), 200f, 1, SpriteMeshType.FullRect, Vector4.zero, false); | |
sprite.name = texture2D.name; | |
promise.Resolve(sprite); | |
}) | |
.Catch(promise.Reject); | |
return promise; | |
} | |
public IPromise<Sprite> GetSprite(GameObject cancelGameObject) | |
{ | |
Promise<Sprite> promise = new Promise<Sprite>(); | |
GetTexture2D(cancelGameObject) | |
.Then(texture2D => | |
{ | |
Sprite sprite = Sprite.Create(texture2D, new Rect(0f, 0f, texture2D.width, texture2D.height), new Vector2(.5f, .5f), 200f, 1, SpriteMeshType.FullRect, Vector4.zero, false); | |
sprite.name = texture2D.name; | |
promise.Resolve(sprite); | |
}) | |
.Catch(promise.Reject); | |
return promise; | |
} | |
public IPromise<Sprite> GetSpriteWhileGameObjectExists(GameObject cancelGameObject) | |
{ | |
return GetSprite(() => cancelGameObject != null); | |
} | |
public IPromise<Sprite> GetSpriteWhileGameObjectIsActive(GameObject cancelGameObject) | |
{ | |
return GetSprite(cancelGameObject); | |
} | |
public void Cancel() | |
{ | |
if (!_isRunning) return; | |
_unityWebRequest.Abort(); | |
} | |
private IEnumerator<float> TextureFromFilePathRoutine(IPendingPromise<Texture2D> promise) { | |
if (FilePath == null) { | |
promise.Reject(new Exception("FilePath is empty")); | |
yield break; | |
} | |
using (_unityWebRequest = UnityWebRequestTexture.GetTexture($"file://{FilePath}")) | |
{ | |
UnityWebRequestAsyncOperation asyncOperation = _unityWebRequest.SendWebRequest(); | |
_isRunning = true; | |
yield return Timing.WaitUntilDone(asyncOperation); | |
if (_unityWebRequest.isNetworkError || _unityWebRequest.isHttpError) { | |
promise.Reject(new Exception($"TextureFromFile.TextureFromFilePathRoutine() failed to load with error: {_unityWebRequest.error}")); | |
} | |
else { | |
Texture2D texture = DownloadHandlerTexture.GetContent(_unityWebRequest); | |
if (texture == null) | |
{ | |
string errorMessage = $"Texture failed to create for {FilePath}: {_unityWebRequest.error}"; | |
Exception exception = new Exception(errorMessage); | |
promise.Reject(exception); | |
yield break; | |
} | |
//name the texture (this is useful when debugging memory | |
Uri uri = new Uri(FilePath); | |
string textureName = uri.Segments[uri.Segments.Length - 1]; | |
texture.name = textureName; | |
promise.Resolve(texture); | |
} | |
_isRunning = false; | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using MEC; | |
using RSG; | |
using UnityEngine; | |
using UnityEngine.Networking; | |
// This one moves the `Texture2D texture = DownloadHandlerTexture.GetContent(_unityWebRequest);` line above the if | |
namespace DefaultNamespace | |
{ | |
public class TextureFromFile | |
{ | |
private string FilePath { get; set; } | |
private UnityWebRequest _unityWebRequest; | |
private bool _isRunning; | |
public TextureFromFile(string filePath) | |
{ | |
FilePath = filePath; | |
_isRunning = false; | |
} | |
public static TextureFromFile AtPath(string filePath) | |
{ | |
return new TextureFromFile(filePath); | |
} | |
public IPromise<Texture2D> GetTexture2D(Func<bool> cancelEvaluator) { | |
if (FilePath == null) | |
{ | |
return Promise<Texture2D>.Rejected(new Exception("FilePath is empty")); | |
} | |
Promise<Texture2D> promise = new Promise<Texture2D>(); | |
Timing.RunCoroutine(TextureFromFilePathRoutine(promise).CancelWith(cancelEvaluator)); | |
return promise; | |
} | |
public IPromise<Texture2D> GetTexture2D(GameObject activeGameObject) { | |
if (FilePath == null) | |
{ | |
return Promise<Texture2D>.Rejected(new Exception("FilePath is empty")); | |
} | |
Promise<Texture2D> promise = new Promise<Texture2D>(); | |
Timing.RunCoroutine(TextureFromFilePathRoutine(promise).CancelWith(activeGameObject)); | |
return promise; | |
} | |
public IPromise<Texture2D> GetTexture2DWhileGameObjectExists(GameObject cancelGameObject) | |
{ | |
return GetTexture2D(() => cancelGameObject != null); | |
} | |
public IPromise<Texture2D> GetTexture2DWhileGameObjectIsActive(GameObject cancelGameObject) | |
{ | |
return GetTexture2D(() => cancelGameObject != null && cancelGameObject.activeInHierarchy); | |
} | |
public IPromise<Sprite> GetSprite(Func<bool> cancelEvaluator) | |
{ | |
Promise<Sprite> promise = new Promise<Sprite>(); | |
GetTexture2D(cancelEvaluator) | |
.Then(texture2D => | |
{ | |
Sprite sprite = Sprite.Create(texture2D, new Rect(0f, 0f, texture2D.width, texture2D.height), new Vector2(.5f, .5f), 200f, 1, SpriteMeshType.FullRect, Vector4.zero, false); | |
sprite.name = texture2D.name; | |
promise.Resolve(sprite); | |
}) | |
.Catch(promise.Reject); | |
return promise; | |
} | |
public IPromise<Sprite> GetSprite(GameObject cancelGameObject) | |
{ | |
Promise<Sprite> promise = new Promise<Sprite>(); | |
GetTexture2D(cancelGameObject) | |
.Then(texture2D => | |
{ | |
Sprite sprite = Sprite.Create(texture2D, new Rect(0f, 0f, texture2D.width, texture2D.height), new Vector2(.5f, .5f), 200f, 1, SpriteMeshType.FullRect, Vector4.zero, false); | |
sprite.name = texture2D.name; | |
promise.Resolve(sprite); | |
}) | |
.Catch(promise.Reject); | |
return promise; | |
} | |
public IPromise<Sprite> GetSpriteWhileGameObjectExists(GameObject cancelGameObject) | |
{ | |
return GetSprite(() => cancelGameObject != null); | |
} | |
public IPromise<Sprite> GetSpriteWhileGameObjectIsActive(GameObject cancelGameObject) | |
{ | |
return GetSprite(cancelGameObject); | |
} | |
public void Cancel() | |
{ | |
if (!_isRunning) return; | |
_unityWebRequest.Abort(); | |
} | |
private IEnumerator<float> TextureFromFilePathRoutine(IPendingPromise<Texture2D> promise) { | |
if (FilePath == null) { | |
promise.Reject(new Exception("FilePath is empty")); | |
yield break; | |
} | |
using (_unityWebRequest = UnityWebRequestTexture.GetTexture($"file://{FilePath}")) | |
{ | |
UnityWebRequestAsyncOperation asyncOperation = _unityWebRequest.SendWebRequest(); | |
_isRunning = true; | |
yield return Timing.WaitUntilDone(asyncOperation); | |
Texture2D texture = DownloadHandlerTexture.GetContent(_unityWebRequest); | |
if (_unityWebRequest.isNetworkError || _unityWebRequest.isHttpError) { | |
promise.Reject(new Exception($"TextureFromFile.TextureFromFilePathRoutine() failed to load with error: {_unityWebRequest.error}")); | |
} | |
else { | |
if (texture == null) | |
{ | |
string errorMessage = $"Texture failed to create for {FilePath}: {_unityWebRequest.error}"; | |
Exception exception = new Exception(errorMessage); | |
promise.Reject(exception); | |
yield break; | |
} | |
//name the texture (this is useful when debugging memory | |
Uri uri = new Uri(FilePath); | |
string textureName = uri.Segments[uri.Segments.Length - 1]; | |
texture.name = textureName; | |
promise.Resolve(texture); | |
} | |
_isRunning = false; | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using MEC; | |
using RSG; | |
using UnityEngine; | |
using UnityEngine.Networking; | |
// Removes unityWebRequest.Abort() but the DownloadHandlerTexture.GetContent call is only in the success path | |
namespace DefaultNamespace | |
{ | |
public class TextureFromFile | |
{ | |
private string FilePath { get; set; } | |
private UnityWebRequest _unityWebRequest; | |
private bool _isRunning; | |
private bool _isCancelled; | |
public TextureFromFile(string filePath) | |
{ | |
FilePath = filePath; | |
_isRunning = false; | |
_isCancelled = false; | |
} | |
public static TextureFromFile AtPath(string filePath) | |
{ | |
return new TextureFromFile(filePath); | |
} | |
public IPromise<Texture2D> GetTexture2D(Func<bool> cancelEvaluator) { | |
if (FilePath == null) | |
{ | |
return Promise<Texture2D>.Rejected(new Exception("FilePath is empty")); | |
} | |
Promise<Texture2D> promise = new Promise<Texture2D>(); | |
Timing.RunCoroutine(TextureFromFilePathRoutine(promise).CancelWith(cancelEvaluator)); | |
return promise; | |
} | |
public IPromise<Texture2D> GetTexture2D(GameObject activeGameObject) { | |
if (FilePath == null) | |
{ | |
return Promise<Texture2D>.Rejected(new Exception("FilePath is empty")); | |
} | |
Promise<Texture2D> promise = new Promise<Texture2D>(); | |
Timing.RunCoroutine(TextureFromFilePathRoutine(promise).CancelWith(activeGameObject)); | |
return promise; | |
} | |
public IPromise<Texture2D> GetTexture2DWhileGameObjectExists(GameObject cancelGameObject) | |
{ | |
return GetTexture2D(() => cancelGameObject != null); | |
} | |
public IPromise<Texture2D> GetTexture2DWhileGameObjectIsActive(GameObject cancelGameObject) | |
{ | |
return GetTexture2D(() => cancelGameObject != null && cancelGameObject.activeInHierarchy); | |
} | |
public IPromise<Sprite> GetSprite(Func<bool> cancelEvaluator) | |
{ | |
Promise<Sprite> promise = new Promise<Sprite>(); | |
GetTexture2D(cancelEvaluator) | |
.Then(texture2D => | |
{ | |
Sprite sprite = Sprite.Create(texture2D, new Rect(0f, 0f, texture2D.width, texture2D.height), new Vector2(.5f, .5f), 200f, 1, SpriteMeshType.FullRect, Vector4.zero, false); | |
sprite.name = texture2D.name; | |
promise.Resolve(sprite); | |
}) | |
.Catch(promise.Reject); | |
return promise; | |
} | |
public IPromise<Sprite> GetSprite(GameObject cancelGameObject) | |
{ | |
Promise<Sprite> promise = new Promise<Sprite>(); | |
GetTexture2D(cancelGameObject) | |
.Then(texture2D => | |
{ | |
Sprite sprite = Sprite.Create(texture2D, new Rect(0f, 0f, texture2D.width, texture2D.height), new Vector2(.5f, .5f), 200f, 1, SpriteMeshType.FullRect, Vector4.zero, false); | |
sprite.name = texture2D.name; | |
promise.Resolve(sprite); | |
}) | |
.Catch(promise.Reject); | |
return promise; | |
} | |
public IPromise<Sprite> GetSpriteWhileGameObjectExists(GameObject cancelGameObject) | |
{ | |
return GetSprite(() => cancelGameObject != null); | |
} | |
public IPromise<Sprite> GetSpriteWhileGameObjectIsActive(GameObject cancelGameObject) | |
{ | |
return GetSprite(cancelGameObject); | |
} | |
public void Cancel() | |
{ | |
if (!_isRunning) return; | |
_isCancelled = true; | |
} | |
private IEnumerator<float> TextureFromFilePathRoutine(IPendingPromise<Texture2D> promise) { | |
if (FilePath == null) { | |
promise.Reject(new Exception("FilePath is empty")); | |
yield break; | |
} | |
using (_unityWebRequest = UnityWebRequestTexture.GetTexture($"file://{FilePath}")) | |
{ | |
UnityWebRequestAsyncOperation asyncOperation = _unityWebRequest.SendWebRequest(); | |
_isRunning = true; | |
yield return Timing.WaitUntilDone(asyncOperation); | |
if (_isCancelled) | |
{ | |
promise.Reject(new Exception($"TextureFromFile.TextureFromFilePathRoutine() cancelled")); | |
} | |
else if (_unityWebRequest.isNetworkError || _unityWebRequest.isHttpError) { | |
promise.Reject(new Exception($"TextureFromFile.TextureFromFilePathRoutine() failed to load with error: {_unityWebRequest.error}")); | |
} | |
else { | |
Texture2D texture = DownloadHandlerTexture.GetContent(_unityWebRequest); | |
if (texture == null) | |
{ | |
string errorMessage = $"Texture failed to create for {FilePath}: {_unityWebRequest.error}"; | |
Exception exception = new Exception(errorMessage); | |
promise.Reject(exception); | |
yield break; | |
} | |
//name the texture (this is useful when debugging memory | |
Uri uri = new Uri(FilePath); | |
string textureName = uri.Segments[uri.Segments.Length - 1]; | |
texture.name = textureName; | |
promise.Resolve(texture); | |
} | |
_isRunning = false; | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using MEC; | |
using RSG; | |
using UnityEngine; | |
using UnityEngine.Networking; | |
// Removes unityWebRequest.Abort() and moves the DownloadHandlerTexture.GetContent call above the if | |
namespace DefaultNamespace | |
{ | |
public class TextureFromFile | |
{ | |
private string FilePath { get; set; } | |
private UnityWebRequest _unityWebRequest; | |
private bool _isRunning; | |
private bool _isCancelled; | |
public TextureFromFile(string filePath) | |
{ | |
FilePath = filePath; | |
_isRunning = false; | |
_isCancelled = false; | |
} | |
public static TextureFromFile AtPath(string filePath) | |
{ | |
return new TextureFromFile(filePath); | |
} | |
public IPromise<Texture2D> GetTexture2D(Func<bool> cancelEvaluator) { | |
if (FilePath == null) | |
{ | |
return Promise<Texture2D>.Rejected(new Exception("FilePath is empty")); | |
} | |
Promise<Texture2D> promise = new Promise<Texture2D>(); | |
Timing.RunCoroutine(TextureFromFilePathRoutine(promise).CancelWith(cancelEvaluator)); | |
return promise; | |
} | |
public IPromise<Texture2D> GetTexture2D(GameObject activeGameObject) { | |
if (FilePath == null) | |
{ | |
return Promise<Texture2D>.Rejected(new Exception("FilePath is empty")); | |
} | |
Promise<Texture2D> promise = new Promise<Texture2D>(); | |
Timing.RunCoroutine(TextureFromFilePathRoutine(promise).CancelWith(activeGameObject)); | |
return promise; | |
} | |
public IPromise<Texture2D> GetTexture2DWhileGameObjectExists(GameObject cancelGameObject) | |
{ | |
return GetTexture2D(() => cancelGameObject != null); | |
} | |
public IPromise<Texture2D> GetTexture2DWhileGameObjectIsActive(GameObject cancelGameObject) | |
{ | |
return GetTexture2D(() => cancelGameObject != null && cancelGameObject.activeInHierarchy); | |
} | |
public IPromise<Sprite> GetSprite(Func<bool> cancelEvaluator) | |
{ | |
Promise<Sprite> promise = new Promise<Sprite>(); | |
GetTexture2D(cancelEvaluator) | |
.Then(texture2D => | |
{ | |
Sprite sprite = Sprite.Create(texture2D, new Rect(0f, 0f, texture2D.width, texture2D.height), new Vector2(.5f, .5f), 200f, 1, SpriteMeshType.FullRect, Vector4.zero, false); | |
sprite.name = texture2D.name; | |
promise.Resolve(sprite); | |
}) | |
.Catch(promise.Reject); | |
return promise; | |
} | |
public IPromise<Sprite> GetSprite(GameObject cancelGameObject) | |
{ | |
Promise<Sprite> promise = new Promise<Sprite>(); | |
GetTexture2D(cancelGameObject) | |
.Then(texture2D => | |
{ | |
Sprite sprite = Sprite.Create(texture2D, new Rect(0f, 0f, texture2D.width, texture2D.height), new Vector2(.5f, .5f), 200f, 1, SpriteMeshType.FullRect, Vector4.zero, false); | |
sprite.name = texture2D.name; | |
promise.Resolve(sprite); | |
}) | |
.Catch(promise.Reject); | |
return promise; | |
} | |
public IPromise<Sprite> GetSpriteWhileGameObjectExists(GameObject cancelGameObject) | |
{ | |
return GetSprite(() => cancelGameObject != null); | |
} | |
public IPromise<Sprite> GetSpriteWhileGameObjectIsActive(GameObject cancelGameObject) | |
{ | |
return GetSprite(cancelGameObject); | |
} | |
public void Cancel() | |
{ | |
if (!_isRunning) return; | |
_isCancelled = true; | |
} | |
private IEnumerator<float> TextureFromFilePathRoutine(IPendingPromise<Texture2D> promise) { | |
if (FilePath == null) { | |
promise.Reject(new Exception("FilePath is empty")); | |
yield break; | |
} | |
using (_unityWebRequest = UnityWebRequestTexture.GetTexture($"file://{FilePath}")) | |
{ | |
UnityWebRequestAsyncOperation asyncOperation = _unityWebRequest.SendWebRequest(); | |
_isRunning = true; | |
yield return Timing.WaitUntilDone(asyncOperation); | |
Texture2D texture = DownloadHandlerTexture.GetContent(_unityWebRequest); | |
if (_isCancelled) | |
{ | |
promise.Reject(new Exception($"TextureFromFile.TextureFromFilePathRoutine() cancelled")); | |
} | |
else if (_unityWebRequest.isNetworkError || _unityWebRequest.isHttpError) { | |
promise.Reject(new Exception($"TextureFromFile.TextureFromFilePathRoutine() failed to load with error: {_unityWebRequest.error}")); | |
} | |
else { | |
if (texture == null) | |
{ | |
string errorMessage = $"Texture failed to create for {FilePath}: {_unityWebRequest.error}"; | |
Exception exception = new Exception(errorMessage); | |
promise.Reject(exception); | |
yield break; | |
} | |
//name the texture (this is useful when debugging memory | |
Uri uri = new Uri(FilePath); | |
string textureName = uri.Segments[uri.Segments.Length - 1]; | |
texture.name = textureName; | |
promise.Resolve(texture); | |
} | |
_isRunning = false; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment