Ejemplo n.º 1
0
        internal bool Load(bool wait, Func <Texture2D> load)
        {
            if (LoadImmediately)
            {
                Texture_Unsafe?.Dispose();
                Texture_Unsafe = load();
                FreeFastTextureLoad();
                return(true);
            }

            // Let's queue a reload onto the main thread and call it a day.
            // Make sure to read the texture size immediately though!
            object queuedLoadLock;

            lock (queuedLoadLock = new object()) {
                _Texture_QueuedLoadLock = queuedLoadLock;

                Func <Texture2D> _load = load;
                load = () => {
                    Texture2D tex;
                    lock (queuedLoadLock) {
                        if (_Texture_QueuedLoadLock != queuedLoadLock)
                        {
                            _load = null;
                            FreeFastTextureLoad();
                            return(Texture_Unsafe);
                        }
                        // NOTE: If something dares to change texture info on the fly, GOOD LUCK.
                        Texture_Unsafe?.Dispose();
                        Texture_Unsafe = tex = _load();
                        FreeFastTextureLoad();
                        _Texture_QueuedLoadLock = null;
                    }
                    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);
                };

                _Texture_QueuedLoad = !_Texture_FTLLoading?
                                      MainThreadHelper.GetForceQueue(load) :
                                          MainThreadHelper.Get(load);
            }

            if (wait || _Texture_Requesting)
            {
                _Texture_QueuedLoad.GetResult();
            }

            return(false);
        }
Ejemplo n.º 2
0
        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);
                        }
Ejemplo n.º 3
0
 public AsyncMaybeBuilder()
 {
     Task = new MaybeAwaitable <T>(new Maybe <T> .MaybeForStateBuilder <T>());
 }
Ejemplo n.º 4
0
        private bool ResetVertexBuffer()
        {
            // Checking for IsDisposed on other threads should be fine...
            if (Vertices != null && !Vertices.IsDisposed && !Vertices.GraphicsDevice.IsDisposed)
            {
                return(false);
            }

            // Handle already queued loads appropriately.
            object queuedLoadLock = _Vertices_QueuedLoadLock;

            if (queuedLoadLock != null)
            {
                lock (queuedLoadLock) {
                    // Queued task finished just in time.
                    if (_Vertices_QueuedLoadLock == null)
                    {
                        return(true);
                    }

                    // If we still can, cancel the queued load, then proceed with lazy-loading.
                    if (MainThreadHelper.IsMainThread)
                    {
                        _Vertices_QueuedLoadLock = null;
                    }
                }

                if (!MainThreadHelper.IsMainThread)
                {
                    // Otherwise wait for it to get loaded, don't reload twice. (Don't wait locked!)
                    while (!_Vertices_QueuedLoad.IsValid)
                    {
                        Thread.Yield();
                    }
                    _Vertices_QueuedLoad.GetResult();
                    return(true);
                }
            }

            if (!(CoreModule.Settings.ThreadedGL ?? Everest.Flags.PreferThreadedGL) && !MainThreadHelper.IsMainThread && queuedLoadLock == null)
            {
                // Let's queue a reload onto the main thread and call it a day.
                lock (queuedLoadLock = new object()) {
                    _Vertices_QueuedLoadLock = queuedLoadLock;
                    _Vertices_QueuedLoad     = MainThreadHelper.Get(() => {
                        lock (queuedLoadLock) {
                            if (_Vertices_QueuedLoadLock == null)
                            {
                                return(Vertices);
                            }
                            // Force-reload as we already returned true on the other thread.
                            Vertices?.Dispose();
                            // NOTE: If something dares to change verts on the fly, make it wait on any existing tasks, then make it force-reload.
                            Vertices = new VertexBuffer(Engine.Graphics.GraphicsDevice, typeof(VertexPositionTexture), verts.Length, BufferUsage.None);
                            Vertices.SetData(verts);
                            _Vertices_QueuedLoadLock = null;
                            return(Vertices);
                        }
                    });
                }
                return(true);
            }

            return(orig_ResetVertexBuffer());
        }
Ejemplo n.º 5
0
        public new void ResetBillboardBuffers()
        {
            // Checking for IsDisposed on other threads should be fine...
            if (billboardVertices != null && !billboardIndices.IsDisposed && !billboardIndices.GraphicsDevice.IsDisposed &&
                billboardVertices != null && !billboardVertices.IsDisposed && !billboardVertices.GraphicsDevice.IsDisposed &&
                billboardInfo.Length <= billboardVertices.VertexCount)
            {
                return;
            }

            // Handle already queued loads appropriately.
            object queuedLoadLock = _Billboard_QueuedLoadLock;

            if (queuedLoadLock != null)
            {
                lock (queuedLoadLock) {
                    // Queued task finished just in time.
                    if (_Billboard_QueuedLoadLock == null)
                    {
                        return;
                    }

                    // If we still can, cancel the queued load, then proceed with lazy-loading.
                    if (MainThreadHelper.IsMainThread)
                    {
                        _Billboard_QueuedLoadLock = null;
                    }
                }

                if (!MainThreadHelper.IsMainThread)
                {
                    // Otherwise wait for it to get loaded, don't reload twice. (Don't wait locked!)
                    while (!_Billboard_QueuedLoad.IsValid)
                    {
                        Thread.Yield();
                    }
                    _Billboard_QueuedLoad.GetResult();
                    return;
                }
            }

            if (!(CoreModule.Settings.ThreadedGL ?? Everest.Flags.PreferThreadedGL) && !MainThreadHelper.IsMainThread && queuedLoadLock == null)
            {
                // Let's queue a reload onto the main thread and call it a day.
                lock (queuedLoadLock = new object()) {
                    _Billboard_QueuedLoadLock = queuedLoadLock;
                    _Billboard_QueuedLoad     = MainThreadHelper.Get(() => {
                        lock (queuedLoadLock) {
                            if (_Billboard_QueuedLoadLock == null)
                            {
                                return(billboardVertices);
                            }
                            // Force-reload as we already returned true on the other thread.
                            if (billboardVertices != null && !billboardVertices.IsDisposed)
                            {
                                billboardVertices.Dispose();
                            }
                            if (billboardIndices != null && !billboardIndices.IsDisposed)
                            {
                                billboardIndices.Dispose();
                            }
                            // NOTE: If something dares to change verts on the fly, make it wait on any existing tasks, then make it force-reload.
                            // Let's rely on the original code for now.
                            orig_ResetBillboardBuffers();
                            _Billboard_QueuedLoadLock = null;
                            return(billboardVertices);
                        }
                    });
                }
                return;
            }

            orig_ResetBillboardBuffers();
        }