예제 #1
0
        internal ScaledTexture(string assetName, SpriteInfo textureWrapper, Bounds sourceRectangle, ulong hash, bool isSprite, bool async, uint expectedScale)
        {
            using var _ = Performance.Track();

            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.Anonymous() ? assetName.SafeName() : source.SafeName();
            originalSize = IsSprite ? sourceRectangle.Extent : new Vector2I(source);

            if (async && Config.AsyncScaling.Enabled)
            {
                ThreadQueue.Queue((SpriteInfo wrapper) => {
                    Thread.CurrentThread.Priority = ThreadPriority.Lowest;
                    using var _ = new AsyncTracker($"Resampling {Name} [{sourceRectangle}]");
                    Thread.CurrentThread.Name = "Texture Resampling Thread";
                    Upscaler.Upscale(
                        texture: this,
                        scale: ref refScale,
                        input: 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());
            }
        }
예제 #2
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);
            }
        }