public ManagedTexture2D(
            ScaledTexture texture,
            Texture2D reference,
            Vector2I dimensions,
            SurfaceFormat format,
            string name = null
            ) : base(reference.GraphicsDevice.IsDisposed ? DrawState.Device : reference.GraphicsDevice, dimensions.Width, dimensions.Height, UseMips, format)
        {
            this.Name = name ?? $"{reference.SafeName()} [RESAMPLED {(CompressionFormat)format}]";

            Reference  = reference.MakeWeak();
            Texture    = texture;
            Dimensions = dimensions - texture.BlockPadding;

            reference.Disposing += (_, _1) => OnParentDispose();

            TotalAllocatedSize += this.SizeBytes();
            ++TotalManagedTextures;

            Garbage.MarkOwned(format, dimensions.Area);
            Disposing += (_, _1) => {
                Garbage.UnmarkOwned(format, dimensions.Area);
                TotalAllocatedSize -= this.SizeBytes();
                --TotalManagedTextures;
            };
        }
Example #2
0
        internal bool TryGet(Texture2D texture, Bounds source, int expectedScale, out ScaledTexture result)
        {
            result = null;

            lock (texture.Meta()) {
                var Map = texture.Meta().SpriteTable;

                using (Lock.Shared) {
                    var rectangleHash = SpriteHash(texture: texture, source: source, expectedScale: expectedScale);
                    if (Map.TryGetValue(rectangleHash, out var scaledTexture))
                    {
                        if (scaledTexture.Texture?.IsDisposed == true)
                        {
                            using (Lock.Promote) {
                                Map.Clear();
                            }
                        }
                        else
                        {
                            if (scaledTexture.IsReady)
                            {
                                result = scaledTexture;
                            }
                            return(true);
                        }
                    }
                }
            }

            return(false);
        }
Example #3
0
        internal void Add(Texture2D reference, ScaledTexture texture, Bounds source, int expectedScale)
        {
            lock (reference.Meta()) {
                var Map           = reference.Meta().SpriteTable;
                var rectangleHash = SpriteHash(texture: reference, source: source, expectedScale: expectedScale);

                using (Lock.Exclusive) {
                    ScaledTextureReferences.Add(texture);
                    Map.Add(rectangleHash, texture);
                }
            }
        }
Example #4
0
        internal void Add(Texture2D reference, ScaledTexture texture, Bounds source, uint expectedScale)
        {
            var rectangleHash = SpriteHash(texture: reference, source: source, expectedScale: expectedScale);

            var meta = reference.Meta();

            using (Lock.Exclusive) {
                ScaledTextureReferences.Add(texture);
                using (meta.Lock.Exclusive) {
                    meta.SpriteTable.Add(rectangleHash, texture);
                }
            }
        }
Example #5
0
        internal static void OnPresent()
        {
            if (TriggerGC)
            {
                ScaledTexture.PurgeTextures((Config.RequiredFreeMemory * Config.RequiredFreeMemoryHysterisis).NearestLong() * 1024 * 1024);
                //Garbage.Collect();
                Garbage.Collect(compact: true, blocking: true, background: false);

                TriggerGC = false;
            }

            if (Config.AsyncScaling.CanFetchAndLoadSameFrame || !PushedUpdateThisFrame)
            {
                ScaledTexture.ProcessPendingActions();
            }
            RemainingTexelFetchBudget = Config.AsyncScaling.ScalingBudgetPerFrameTexels;
            FetchedThisFrame          = false;
            PushedUpdateThisFrame     = false;
            ++CurrentFrame;
        }
Example #6
0
        internal void Remove(ScaledTexture scaledTexture, Texture2D texture)
        {
            try {
                lock (texture.Meta()) {
                    var Map = texture.Meta().SpriteTable;

                    using (Lock.Exclusive) {
                        try {
                            ScaledTextureReferences.Purge();
                            var removeElements = new List <ScaledTexture>();
                            foreach (var element in ScaledTextureReferences)
                            {
                                if (element == scaledTexture)
                                {
                                    removeElements.Add(element);
                                }
                            }

                            foreach (var element in removeElements)
                            {
                                ScaledTextureReferences.Remove(element);
                            }
                        }
                        catch { }

                        Map.Clear();
                    }
                }
            }
            finally {
                if (scaledTexture.Texture != null && !scaledTexture.Texture.IsDisposed)
                {
                    Debug.TraceLn($"Disposing Active HD Texture: {scaledTexture.SafeName()}");

                    //scaledTexture.Texture.Dispose();
                }
            }
        }
Example #7
0
        static internal ScaledTexture Get(Texture2D texture, Bounds source, int expectedScale)
        {
            if (!Validate(texture))
            {
                return(null);
            }

            if (SpriteMap.TryGet(texture, source, expectedScale, out var scaleTexture))
            {
                return(scaleTexture);
            }

            bool useAsync = (Config.AsyncScaling.EnabledForUnknownTextures || !texture.Name.IsBlank()) && (texture.Area() >= Config.AsyncScaling.MinimumSizeTexels);

            if (useAsync && Config.AsyncScaling.Enabled && !DrawState.GetUpdateToken(texture.Area()) && !texture.Meta().HasCachedData)
            {
                return(null);
            }

            if (Config.DiscardDuplicates)
            {
                // Check for duplicates with the same name.
                // TODO : We do have a synchronity issue here. We could purge before an asynchronous task adds the texture.
                // DiscardDuplicatesFrameDelay
                if (!texture.Name.IsBlank() && !Config.DiscardDuplicatesBlacklist.Contains(texture.SafeName()))
                {
                    try {
                        lock (DuplicateTable) {
                            if (DuplicateTable.TryGetValue(texture.SafeName(), out var weakTexture))
                            {
                                if (weakTexture.TryGetTarget(out var strongTexture))
                                {
                                    // Is it not the same texture, and the previous texture has not been accessed for at least 2 frames?
                                    if (strongTexture != texture && (DrawState.CurrentFrame - strongTexture.Meta().LastAccessFrame) > 2)
                                    {
                                        Debug.TraceLn($"Purging Duplicate Texture '{strongTexture.SafeName()}'");
                                        DuplicateTable[texture.SafeName()] = texture.MakeWeak();
                                        Purge(strongTexture);
                                    }
                                }
                                else
                                {
                                    DuplicateTable[texture.SafeName()] = texture.MakeWeak();
                                }
                            }
                            else
                            {
                                DuplicateTable.Add(texture.SafeName(), texture.MakeWeak());
                            }
                        }
                    }
                    catch (Exception ex) {
                        ex.PrintWarning();
                    }
                }
            }

            bool  isSprite       = !ExcludeSprite(texture) && (!source.Offset.IsZero || source.Extent != texture.Extent());
            var   textureWrapper = new SpriteInfo(reference: texture, dimensions: source, expectedScale: expectedScale);
            ulong hash           = Upscaler.GetHash(textureWrapper, isSprite);

            var newTexture = new ScaledTexture(
                assetName: texture.SafeName(),
                textureWrapper: textureWrapper,
                sourceRectangle: source,
                isSprite: isSprite,
                hash: hash,
                async: useAsync,
                expectedScale: expectedScale
                );

            if (useAsync && Config.AsyncScaling.Enabled)
            {
                // It adds itself to the relevant maps.
                if (newTexture.IsReady && newTexture.Texture != null)
                {
                    return(newTexture);
                }
                return(null);
            }
            else
            {
                return(newTexture);
            }
        }
Example #8
0
 static private ulong SpriteHash(Texture2D texture, Bounds source, int expectedScale)
 {
     return(Hashing.CombineHash(ScaledTexture.ExcludeSprite(texture) ? 0UL : source.Hash(), expectedScale.GetHashCode()));
 }
Example #9
0
 private static unsafe void CreateNewTexture(
     ScaledTexture texture,
     SpriteInfo input,
     bool desprite,
     bool isWater,
     in Bounds spriteBounds,
Example #10
0
        static internal ScaledTexture Get(Texture2D texture, Bounds source, uint expectedScale)
        {
            using var _ = Performance.Track();

            if (SpriteMap.TryGet(texture, source, expectedScale, out var scaleTexture))
            {
                return(scaleTexture);
            }

            if (!Validate(texture))
            {
                return(null);
            }

            bool useAsync = (Config.AsyncScaling.EnabledForUnknownTextures || !texture.Anonymous()) && (texture.Area() >= Config.AsyncScaling.MinimumSizeTexels);
            // !texture.Meta().HasCachedData

            var         estimatedDuration = GetTimer(texture: texture, async: useAsync).Estimate(texture.Area());
            const float multiplier        = 0.75f;
            var         remainingTime     = DrawState.RemainingFrameTime(multiplier: multiplier);

            if (DrawState.PushedUpdateWithin(1) && estimatedDuration >= remainingTime)
            {
                return(null);
            }

            // TODO : We should really only populate the average when we are performing an expensive operation like GetData.
            var begin = DateTime.Now;

            bool       isSprite = (!source.Offset.IsZero || source.Extent != texture.Extent());
            SpriteInfo textureWrapper;
            ulong      hash;

            using (Performance.Track("new SpriteInfo"))
                textureWrapper = new SpriteInfo(reference: texture, dimensions: source, expectedScale: expectedScale);

            // If this is null, it can only happen due to something being blocked, so we should try again later.
            if (textureWrapper.Data == null)
            {
                return(null);
            }

            DrawState.PushedUpdateThisFrame = true;

            try {
                using (Performance.Track("Upscaler.GetHash"))
                    hash = Upscaler.GetHash(textureWrapper, isSprite);

                var newTexture = new ScaledTexture(
                    assetName: texture.SafeName(),
                    textureWrapper: textureWrapper,
                    sourceRectangle: source,
                    isSprite: isSprite,
                    hash: hash,
                    async: useAsync,
                    expectedScale: expectedScale
                    );
                if (useAsync && Config.AsyncScaling.Enabled)
                {
                    // It adds itself to the relevant maps.
                    if (newTexture.IsReady && newTexture.Texture != null)
                    {
                        return(newTexture);
                    }
                    return(null);
                }
                else
                {
                    return(newTexture);
                }
            }
            finally {
                var averager = GetTimer(cached: textureWrapper.WasCached, async: useAsync);
                averager.Add(texture.Area(), DateTime.Now - begin);
            }
        }