Example #1
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 #2
0
 internal static long SizeBytes(this Texture2D texture)
 {
     return(texture.Format.SizeBytes(texture.Area()));
 }
Example #3
0
        internal static bool Validate(Texture2D texture)
        {
            int textureArea = texture.Area();

            if (texture is ManagedTexture2D)
            {
                if (!texture.Meta().TracePrinted)
                {
                    texture.Meta().TracePrinted = true;
                    Debug.TraceLn($"Not Scaling Texture '{texture.SafeName()}', is already scaled");
                }
                return(false);
            }

            if (texture is RenderTarget2D)
            {
                if (!texture.Meta().TracePrinted)
                {
                    texture.Meta().TracePrinted = true;
                    Debug.TraceLn($"Not Scaling Texture '{texture.SafeName()}', render targets unsupported");
                }
                return(false);
            }

            if (Math.Max(texture.Width, texture.Height) <= Config.Resample.MinimumTextureDimensions)
            {
                if (!texture.Meta().TracePrinted)
                {
                    texture.Meta().TracePrinted = true;
                    Debug.TraceLn($"Not Scaling Texture '{texture.SafeName()}', texture is too small to qualify");
                }
                return(false);
            }

            if (textureArea == 0)
            {
                if (!texture.Meta().TracePrinted)
                {
                    texture.Meta().TracePrinted = true;
                    Debug.TraceLn($"Not Scaling Texture '{texture.SafeName()}', zero area");
                }
                return(false);
            }

            // TODO pComPtr check?
            if (texture.IsDisposed || texture.GraphicsDevice.IsDisposed)
            {
                if (!texture.Meta().TracePrinted)
                {
                    texture.Meta().TracePrinted = true;
                    Debug.TraceLn($"Not Scaling Texture '{texture.SafeName()}', Is Zombie");
                }
                return(false);
            }

            if (Config.IgnoreUnknownTextures && texture.Name.IsBlank())
            {
                if (!texture.Meta().TracePrinted)
                {
                    texture.Meta().TracePrinted = true;
                    Debug.TraceLn($"Not Scaling Texture '{texture.SafeName()}', Is Unknown Texture");
                }
                return(false);
            }


            if (texture.LevelCount > 1)
            {
                if (!texture.Meta().TracePrinted)
                {
                    texture.Meta().TracePrinted = true;
                    Debug.TraceLn($"Not Scaling Texture '{texture.SafeName()}', Multi-Level Textures Unsupported: {texture.LevelCount} levels");
                }
                return(false);
            }

            if (!LegalFormat(texture))
            {
                if (!texture.Meta().TracePrinted)
                {
                    texture.Meta().TracePrinted = true;
                    Debug.TraceLn($"Not Scaling Texture '{texture.SafeName()}', Format Unsupported: {texture.Format}");
                }
                return(false);
            }

            if (!texture.Name.IsBlank())
            {
                foreach (var blacklisted in Config.Resample.Blacklist)
                {
                    if (texture.SafeName().StartsWith(blacklisted))
                    {
                        if (!texture.Meta().TracePrinted)
                        {
                            texture.Meta().TracePrinted = true;
                            Debug.TraceLn($"Not Scaling Texture '{texture.SafeName()}', Is Blacklisted");
                        }
                        return(false);
                    }
                }
            }

            return(true);
        }
Example #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);
            }
        }