Ejemplo n.º 1
0
        internal ScaledTexture(string assetName, SpriteInfo textureWrapper, Bounds sourceRectangle, ulong hash, bool isSprite, bool async, int expectedScale)
        {
            IsSprite = isSprite;
            Hash     = hash;
            var source = textureWrapper.Reference;

            this.OriginalSourceRectangle = new Bounds(sourceRectangle);
            this.Reference       = source.MakeWeak();
            this.sourceRectangle = sourceRectangle;
            this.refScale        = expectedScale;
            SpriteMap.Add(source, this, sourceRectangle, expectedScale);

            this.Name    = source.Name.IsBlank() ? assetName : source.SafeName();
            originalSize = IsSprite ? sourceRectangle.Extent : new Vector2I(source);

            if (async && Config.AsyncScaling.Enabled)
            {
                ThreadPool.QueueUserWorkItem((object wrapper) => {
                    Thread.CurrentThread.Priority = ThreadPriority.Lowest;
                    Thread.CurrentThread.Name     = "Texture Resampling Thread";
                    Upscaler.Upscale(
                        texture: this,
                        scale: ref refScale,
                        input: (SpriteInfo)wrapper,
                        desprite: IsSprite,
                        hash: Hash,
                        wrapped: ref Wrapped,
                        async: true
                        );
                    // If the upscale fails, the asynchronous action on the render thread automatically cleans up the ScaledTexture.
                }, textureWrapper);
            }
            else
            {
                // TODO store the HD Texture in _this_ object instead. Will confuse things like subtexture updates, though.
                this.Texture = Upscaler.Upscale(
                    texture: this,
                    scale: ref refScale,
                    input: textureWrapper,
                    desprite: IsSprite,
                    hash: Hash,
                    wrapped: ref Wrapped,
                    async: false
                    );

                if (this.Texture != null)
                {
                    Finish();
                }
                else
                {
                    Dispose();
                    return;
                }
            }

            // TODO : I would love to dispose of this texture _now_, but we rely on it disposing to know if we need to dispose of ours.
            // There is a better way to do this using weak references, I just need to analyze it further. Memory leaks have been a pain so far.
            source.Disposing += (object sender, EventArgs args) => { OnParentDispose((Texture2D)sender); };

            lock (MostRecentList) {
                CurrentRecentNode = MostRecentList.AddFirst(this.MakeWeak());
            }
        }
Ejemplo n.º 2
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);
            }
        }
Ejemplo n.º 3
0
 internal static void Purge(Texture2D reference, Bounds?bounds, DataRef <byte> data)
 {
     SpriteInfo.Purge(reference, bounds, data);
     SpriteMap.Purge(reference, bounds);
     Upscaler.PurgeHash(reference);
 }
Ejemplo n.º 4
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);
            }
        }