internal override unsafe void Reload() { // Unload task might end up conflicting with Reload - let's instead force-unload in Load. // Unload(); // Handle already queued loads appropriately. object queuedLoadLock = _Texture_QueuedLoadLock; if (queuedLoadLock != null && !_Texture_Reloading) { lock (queuedLoadLock) { // Queued task finished just in time. if (_Texture_QueuedLoadLock != queuedLoadLock) { return; } // If we still can, cancel the queued load, then proceed with lazy-loading. if (MainThreadHelper.IsMainThread) { _Texture_QueuedLoadLock = null; } } if (!MainThreadHelper.IsMainThread) { // Otherwise wait for it to get loaded, don't reload twice. (Don't wait locked!) while (!_Texture_QueuedLoad.IsValid) { Thread.Yield(); } _Texture_QueuedLoad.GetResult(); return; } } if (ftlEnabled && CanPreload && (Metadata?.StreamAsync ?? true) && !_Texture_Reloading && !_Texture_Requesting) { // Preload as we need to know the texture size WITHOUT ALLOCATING SPACE. // ... also because the texture size is required in the calling ctx past Reload. if (Preload(true)) { CountFastTextureLoad(); lock (queuedLoadLock = new object()) { _Texture_QueuedLoadLock = queuedLoadLock; _Texture_QueuedLoad = new MaybeAwaitable <Texture2D>(Task.Run(() => { try { lock (queuedLoadLock) { // Queued load cancelled or replaced with another queued load. if (_Texture_QueuedLoadLock != queuedLoadLock) { FreeFastTextureLoad(); return(Texture_Unsafe); } GrabFastTextureLoad(); // NOTE: If something dares to change texture info on the fly, GOOD LUCK. _Texture_Reloading = true; Reload(); if (_Texture_QueuedLoadLock == queuedLoadLock) { _Texture_QueuedLoadLock = null; } Texture2D tex = Texture_Unsafe; if (_Texture_UnloadAfterReload) { tex?.Dispose(); tex = Texture_Unsafe; // ... can anything even swap the texture here? Texture_Unsafe = null; tex?.Dispose(); _Texture_UnloadAfterReload = false; } return(tex); } } catch (Exception e) { Celeste.patch_Celeste.CriticalFailureHandler(e); throw; } }).GetAwaiter()); return; } } } _Texture_Reloading = false; if (Metadata != null) { if (Metadata.StreamAsync || MainThreadHelper.IsMainThread) { Stream stream = Metadata.Stream; if (stream != null) { using (stream) { bool premul = false; // Assume unpremultiplied by default. if (Metadata.TryGetMeta(out TextureMeta meta)) { premul = meta.Premultiplied; } if (ContentExtensions.TextureSetDataSupportsPtr) { int w, h; IntPtr dataPtr; if (premul) { ContentExtensions.LoadTextureRaw(Celeste.Celeste.Instance.GraphicsDevice, stream, out w, out h, out dataPtr); } else { ContentExtensions.LoadTextureLazyPremultiply(Celeste.Celeste.Instance.GraphicsDevice, stream, out w, out h, out dataPtr); } stream.Dispose(); Width = w; Height = h; Load(false, () => { Texture2D tex = new Texture2D(Celeste.Celeste.Instance.GraphicsDevice, w, h, false, SurfaceFormat.Color); tex.SetData(dataPtr); ContentExtensions.UnloadTextureRaw(dataPtr); return(tex); }); } else { int w, h; byte[] data; if (premul) { ContentExtensions.LoadTextureRaw(Celeste.Celeste.Instance.GraphicsDevice, stream, out w, out h, out data); } else { ContentExtensions.LoadTextureLazyPremultiply(Celeste.Celeste.Instance.GraphicsDevice, stream, out w, out h, out data); } stream.Dispose(); Width = w; Height = h; Load(false, () => { Texture2D tex = new Texture2D(Celeste.Celeste.Instance.GraphicsDevice, w, h, false, SurfaceFormat.Color); tex.SetData(data); data = null; return(tex); }); } } } else if (Fallback != null) { ((patch_VirtualTexture)(object)Fallback).Reload(); Texture_Unsafe = Fallback.Texture; } } else { // This is ugly but if the asset doesn't like multithreading, so be it. // Not even preloading will be beneficial here, and forget about GetMeta. Load(true, () => { using (Stream stream = Metadata.Stream) { if (stream != null) { bool premul = false; // Assume unpremultiplied by default. if (Metadata.TryGetMeta(out TextureMeta meta)) { premul = meta.Premultiplied; } if (premul) { Texture2D tex = Texture2D.FromStream(Celeste.Celeste.Instance.GraphicsDevice, stream); return(tex); } else if (ContentExtensions.TextureSetDataSupportsPtr) { ContentExtensions.LoadTextureLazyPremultiply(Celeste.Celeste.Instance.GraphicsDevice, stream, out int w, out int h, out IntPtr dataPtr); Texture2D tex = new Texture2D(Celeste.Celeste.Instance.GraphicsDevice, w, h, false, SurfaceFormat.Color); tex.SetData(dataPtr); ContentExtensions.UnloadTextureRaw(dataPtr); return(tex); } else { ContentExtensions.LoadTextureLazyPremultiply(Celeste.Celeste.Instance.GraphicsDevice, stream, out int w, out int h, out byte[] data); Texture2D tex = new Texture2D(Celeste.Celeste.Instance.GraphicsDevice, w, h, false, SurfaceFormat.Color); tex.SetData(data); data = null; return(tex); } } else if (Fallback != null) { ((patch_VirtualTexture)(object)Fallback).Reload(); return(Fallback.Texture); } return(null); }