Exemple #1
0
        /// <summary>
        /// Constructs a new instance of the cached GPU texture.
        /// </summary>
        /// <param name="context">GPU context that the texture belongs to</param>
        /// <param name="info">Texture information</param>
        /// <param name="sizeInfo">Size information of the texture</param>
        /// <param name="scaleMode">The scale mode to initialize with. If scaled, the texture's data is loaded immediately and scaled up</param>
        public Texture(GpuContext context, TextureInfo info, SizeInfo sizeInfo, TextureScaleMode scaleMode)
        {
            ScaleFactor = 1f; // Texture is first loaded at scale 1x.
            ScaleMode   = scaleMode;

            InitializeTexture(context, info, sizeInfo);
        }
Exemple #2
0
        public static Texture Scale(this Texture texture, int width, int height, TextureScaleMode scaleMode)
        {
            Texture2D texture2D = (Texture2D)TextureUtils.ConvertToReadableTexture(texture);

            texture2D.Scale2D(width, height, scaleMode);

            texture = texture2D;

            return(texture);
        }
Exemple #3
0
        /// <summary>
        /// Sets the Scale Factor on this texture, and immediately recreates it at the correct size.
        /// When a texture is resized, a scaled copy is performed from the old texture to the new one, to ensure no data is lost.
        /// If scale is equivalent, this only propagates the blacklisted/scaled mode.
        /// If called on a view, its storage is resized instead.
        /// When resizing storage, all texture views are recreated.
        /// </summary>
        /// <param name="scale">The new scale factor for this texture</param>
        public void SetScale(float scale)
        {
            TextureScaleMode newScaleMode = ScaleMode == TextureScaleMode.Blacklisted ? ScaleMode : TextureScaleMode.Scaled;

            if (_viewStorage != this)
            {
                _viewStorage.ScaleMode = newScaleMode;
                _viewStorage.SetScale(scale);
                return;
            }

            if (ScaleFactor != scale)
            {
                Logger.Debug?.Print(LogClass.Gpu, $"Rescaling {Info.Width}x{Info.Height} {Info.FormatInfo.Format.ToString()} to ({ScaleFactor} to {scale}). ");

                ScaleFactor = scale;

                ITexture newStorage = GetScaledHostTexture(ScaleFactor);

                Logger.Debug?.Print(LogClass.Gpu, $"  Copy performed: {HostTexture.Width}x{HostTexture.Height} to {newStorage.Width}x{newStorage.Height}");

                ReplaceStorage(newStorage);

                // All views must be recreated against the new storage.

                foreach (var view in _views)
                {
                    Logger.Debug?.Print(LogClass.Gpu, $"  Recreating view {Info.Width}x{Info.Height} {Info.FormatInfo.Format.ToString()}.");
                    view.ScaleFactor = scale;

                    TextureCreateInfo viewCreateInfo = TextureManager.GetCreateInfo(view.Info, _context.Capabilities);

                    ITexture newView = HostTexture.CreateView(viewCreateInfo, view._firstLayer - _firstLayer, view._firstLevel - _firstLevel);

                    view.ReplaceStorage(newView);

                    view.ScaleMode = newScaleMode;
                }
            }

            if (ScaleMode != newScaleMode)
            {
                ScaleMode = newScaleMode;

                foreach (var view in _views)
                {
                    view.ScaleMode = newScaleMode;
                }
            }
        }
Exemple #4
0
        /// <summary>
        /// Constructs a new instance of the cached GPU texture.
        /// </summary>
        /// <param name="context">GPU context that the texture belongs to</param>
        /// <param name="info">Texture information</param>
        /// <param name="sizeInfo">Size information of the texture</param>
        /// <param name="firstLayer">The first layer of the texture, or 0 if the texture has no parent</param>
        /// <param name="firstLevel">The first mipmap level of the texture, or 0 if the texture has no parent</param>
        /// <param name="scaleFactor">The floating point scale factor to initialize with</param>
        /// <param name="scaleMode">The scale mode to initialize with</param>
        private Texture(
            GpuContext context,
            TextureInfo info,
            SizeInfo sizeInfo,
            int firstLayer,
            int firstLevel,
            float scaleFactor,
            TextureScaleMode scaleMode)
        {
            InitializeTexture(context, info, sizeInfo);

            _firstLayer = firstLayer;
            _firstLevel = firstLevel;

            ScaleFactor = scaleFactor;
            ScaleMode   = scaleMode;

            InitializeData(true);
        }
Exemple #5
0
        /// <summary>
        /// Tries to find an existing texture, or create a new one if not found.
        /// </summary>
        /// <param name="info">Texture information of the texture to be found or created</param>
        /// <param name="flags">The texture search flags, defines texture comparison rules</param>
        /// <returns>The texture</returns>
        public Texture FindOrCreateTexture(TextureInfo info, TextureSearchFlags flags = TextureSearchFlags.None)
        {
            bool isSamplerTexture = (flags & TextureSearchFlags.ForSampler) != 0;

            bool isScalable = IsUpscaleCompatible(info);

            TextureScaleMode scaleMode = TextureScaleMode.Blacklisted;

            if (isScalable)
            {
                scaleMode = (flags & TextureSearchFlags.WithUpscale) != 0 ? TextureScaleMode.Scaled : TextureScaleMode.Eligible;
            }

            int sameAddressOverlapsCount;

            lock (_textures)
            {
                // Try to find a perfect texture match, with the same address and parameters.
                sameAddressOverlapsCount = _textures.FindOverlaps(info.Address, ref _textureOverlaps);
            }

            for (int index = 0; index < sameAddressOverlapsCount; index++)
            {
                Texture overlap = _textureOverlaps[index];

                if (overlap.IsPerfectMatch(info, flags))
                {
                    if (!isSamplerTexture)
                    {
                        // If not a sampler texture, it is managed by the auto delete
                        // cache, ensure that it is on the "top" of the list to avoid
                        // deletion.
                        _cache.Lift(overlap);
                    }
                    else if (!TextureCompatibility.SizeMatches(overlap.Info, info))
                    {
                        // If this is used for sampling, the size must match,
                        // otherwise the shader would sample garbage data.
                        // To fix that, we create a new texture with the correct
                        // size, and copy the data from the old one to the new one.
                        overlap.ChangeSize(info.Width, info.Height, info.DepthOrLayers);
                    }

                    overlap.SynchronizeMemory();

                    return(overlap);
                }
            }

            // Calculate texture sizes, used to find all overlapping textures.
            SizeInfo sizeInfo;

            if (info.Target == Target.TextureBuffer)
            {
                sizeInfo = new SizeInfo(info.Width * info.FormatInfo.BytesPerPixel);
            }
            else if (info.IsLinear)
            {
                sizeInfo = SizeCalculator.GetLinearTextureSize(
                    info.Stride,
                    info.Height,
                    info.FormatInfo.BlockHeight);
            }
            else
            {
                sizeInfo = SizeCalculator.GetBlockLinearTextureSize(
                    info.Width,
                    info.Height,
                    info.GetDepth(),
                    info.Levels,
                    info.GetLayers(),
                    info.FormatInfo.BlockWidth,
                    info.FormatInfo.BlockHeight,
                    info.FormatInfo.BytesPerPixel,
                    info.GobBlocksInY,
                    info.GobBlocksInZ,
                    info.GobBlocksInTileX);
            }

            // Find view compatible matches.
            ulong size = (ulong)sizeInfo.TotalSize;
            int   overlapsCount;

            lock (_textures)
            {
                overlapsCount = _textures.FindOverlaps(info.Address, size, ref _textureOverlaps);
            }

            Texture texture = null;

            for (int index = 0; index < overlapsCount; index++)
            {
                Texture overlap = _textureOverlaps[index];

                if (overlap.IsViewCompatible(info, size, out int firstLayer, out int firstLevel) == TextureViewCompatibility.Full)
                {
                    if (!isSamplerTexture)
                    {
                        info = AdjustSizes(overlap, info, firstLevel);
                    }

                    texture = overlap.CreateView(info, sizeInfo, firstLayer, firstLevel);

                    if (IsTextureModified(overlap))
                    {
                        texture.SignalModified();
                    }

                    // The size only matters (and is only really reliable) when the
                    // texture is used on a sampler, because otherwise the size will be
                    // aligned.
                    if (!TextureCompatibility.SizeMatches(overlap.Info, info, firstLevel) && isSamplerTexture)
                    {
                        texture.ChangeSize(info.Width, info.Height, info.DepthOrLayers);
                    }

                    break;
                }
            }

            // No match, create a new texture.
            if (texture == null)
            {
                texture = new Texture(_context, info, sizeInfo, scaleMode);

                // Step 1: Find textures that are view compatible with the new texture.
                // Any textures that are incompatible will contain garbage data, so they should be removed where possible.

                int  viewCompatible = 0;
                bool setData        = isSamplerTexture || overlapsCount == 0;

                for (int index = 0; index < overlapsCount; index++)
                {
                    Texture overlap        = _textureOverlaps[index];
                    bool    overlapInCache = overlap.CacheNode != null;

                    TextureViewCompatibility compatibility = texture.IsViewCompatible(overlap.Info, overlap.Size, out int firstLayer, out int firstLevel);

                    if (compatibility != TextureViewCompatibility.Incompatible)
                    {
                        if (_overlapInfo.Length != _textureOverlaps.Length)
                        {
                            Array.Resize(ref _overlapInfo, _textureOverlaps.Length);
                        }

                        _overlapInfo[viewCompatible]       = new OverlapInfo(compatibility, firstLayer, firstLevel);
                        _textureOverlaps[viewCompatible++] = overlap;
                    }
                    else if (overlapInCache || !setData)
                    {
                        if (info.GobBlocksInZ > 1 && info.GobBlocksInZ == overlap.Info.GobBlocksInZ)
                        {
                            // Allow overlapping slices of 3D textures. Could be improved in future by making sure the textures don't overlap.
                            continue;
                        }

                        // The overlap texture is going to contain garbage data after we draw, or is generally incompatible.
                        // If the texture cannot be entirely contained in the new address space, and one of its view children is compatible with us,
                        // it must be flushed before removal, so that the data is not lost.

                        // If the texture was modified since its last use, then that data is probably meant to go into this texture.
                        // If the data has been modified by the CPU, then it also shouldn't be flushed.
                        bool modified = overlap.ConsumeModified();

                        bool flush = overlapInCache && !modified && (overlap.Address <texture.Address || overlap.EndAddress> texture.EndAddress) && overlap.HasViewCompatibleChild(texture);

                        setData |= modified || flush;

                        if (overlapInCache)
                        {
                            _cache.Remove(overlap, flush);
                        }
                    }
                }

                // We need to synchronize before copying the old view data to the texture,
                // otherwise the copied data would be overwritten by a future synchronization.
                texture.InitializeData(false, setData);

                for (int index = 0; index < viewCompatible; index++)
                {
                    Texture     overlap = _textureOverlaps[index];
                    OverlapInfo oInfo   = _overlapInfo[index];

                    if (oInfo.Compatibility != TextureViewCompatibility.Full)
                    {
                        continue; // Copy only compatibilty.
                    }

                    TextureInfo overlapInfo = AdjustSizes(texture, overlap.Info, oInfo.FirstLevel);

                    TextureCreateInfo createInfo = GetCreateInfo(overlapInfo, _context.Capabilities);

                    if (texture.ScaleFactor != overlap.ScaleFactor)
                    {
                        // A bit tricky, our new texture may need to contain an existing texture that is upscaled, but isn't itself.
                        // In that case, we prefer the higher scale only if our format is render-target-like, otherwise we scale the view down before copy.

                        texture.PropagateScale(overlap);
                    }

                    ITexture newView = texture.HostTexture.CreateView(createInfo, oInfo.FirstLayer, oInfo.FirstLevel);

                    overlap.HostTexture.CopyTo(newView, 0, 0);

                    // Inherit modification from overlapping texture, do that before replacing
                    // the view since the replacement operation removes it from the list.
                    if (IsTextureModified(overlap))
                    {
                        texture.SignalModified();
                    }

                    overlap.ReplaceView(texture, overlapInfo, newView, oInfo.FirstLayer, oInfo.FirstLevel);
                }

                // If the texture is a 3D texture, we need to additionally copy any slice
                // of the 3D texture to the newly created 3D texture.
                if (info.Target == Target.Texture3D)
                {
                    for (int index = 0; index < viewCompatible; index++)
                    {
                        Texture     overlap = _textureOverlaps[index];
                        OverlapInfo oInfo   = _overlapInfo[index];

                        if (oInfo.Compatibility != TextureViewCompatibility.Incompatible)
                        {
                            overlap.BlacklistScale();

                            overlap.HostTexture.CopyTo(texture.HostTexture, oInfo.FirstLayer, oInfo.FirstLevel);

                            if (IsTextureModified(overlap))
                            {
                                texture.SignalModified();
                            }
                        }
                    }
                }
            }

            // Sampler textures are managed by the texture pool, all other textures
            // are managed by the auto delete cache.
            if (!isSamplerTexture)
            {
                _cache.Add(texture);
                texture.Modified += CacheTextureModified;
                texture.Disposed += CacheTextureDisposed;
            }

            lock (_textures)
            {
                _textures.Add(texture);
            }

            ShrinkOverlapsBufferIfNeeded();

            return(texture);
        }
Exemple #6
0
        /// <summary>
        /// Tries to find an existing texture, or create a new one if not found.
        /// </summary>
        /// <param name="info">Texture information of the texture to be found or created</param>
        /// <param name="flags">The texture search flags, defines texture comparison rules</param>
        /// <returns>The texture</returns>
        public Texture FindOrCreateTexture(TextureInfo info, TextureSearchFlags flags = TextureSearchFlags.None)
        {
            bool isSamplerTexture = (flags & TextureSearchFlags.Sampler) != 0;

            bool isScalable = IsUpscaleCompatible(info);

            TextureScaleMode scaleMode = TextureScaleMode.Blacklisted;

            if (isScalable)
            {
                scaleMode = (flags & TextureSearchFlags.WithUpscale) != 0 ? TextureScaleMode.Scaled : TextureScaleMode.Eligible;
            }

            // Try to find a perfect texture match, with the same address and parameters.
            int sameAddressOverlapsCount = _textures.FindOverlaps(info.Address, ref _textureOverlaps);

            for (int index = 0; index < sameAddressOverlapsCount; index++)
            {
                Texture overlap = _textureOverlaps[index];

                if (overlap.IsPerfectMatch(info, flags))
                {
                    if (!isSamplerTexture)
                    {
                        // If not a sampler texture, it is managed by the auto delete
                        // cache, ensure that it is on the "top" of the list to avoid
                        // deletion.
                        _cache.Lift(overlap);
                    }
                    else if (!overlap.SizeMatches(info))
                    {
                        // If this is used for sampling, the size must match,
                        // otherwise the shader would sample garbage data.
                        // To fix that, we create a new texture with the correct
                        // size, and copy the data from the old one to the new one.
                        overlap.ChangeSize(info.Width, info.Height, info.DepthOrLayers);
                    }

                    overlap.SynchronizeMemory();

                    return(overlap);
                }
            }

            // Calculate texture sizes, used to find all overlapping textures.
            SizeInfo sizeInfo;

            if (info.Target == Target.TextureBuffer)
            {
                sizeInfo = new SizeInfo(info.Width * info.FormatInfo.BytesPerPixel);
            }
            else if (info.IsLinear)
            {
                sizeInfo = SizeCalculator.GetLinearTextureSize(
                    info.Stride,
                    info.Height,
                    info.FormatInfo.BlockHeight);
            }
            else
            {
                sizeInfo = SizeCalculator.GetBlockLinearTextureSize(
                    info.Width,
                    info.Height,
                    info.GetDepth(),
                    info.Levels,
                    info.GetLayers(),
                    info.FormatInfo.BlockWidth,
                    info.FormatInfo.BlockHeight,
                    info.FormatInfo.BytesPerPixel,
                    info.GobBlocksInY,
                    info.GobBlocksInZ,
                    info.GobBlocksInTileX);
            }

            // Find view compatible matches.
            ulong size = (ulong)sizeInfo.TotalSize;

            int overlapsCount = _textures.FindOverlaps(info.Address, size, ref _textureOverlaps);

            Texture texture = null;

            for (int index = 0; index < overlapsCount; index++)
            {
                Texture overlap = _textureOverlaps[index];

                if (overlap.IsViewCompatible(info, size, out int firstLayer, out int firstLevel))
                {
                    if (!isSamplerTexture)
                    {
                        info = AdjustSizes(overlap, info, firstLevel);
                    }

                    texture = overlap.CreateView(info, sizeInfo, firstLayer, firstLevel);

                    if (IsTextureModified(overlap))
                    {
                        CacheTextureModified(texture);
                    }

                    // The size only matters (and is only really reliable) when the
                    // texture is used on a sampler, because otherwise the size will be
                    // aligned.
                    if (!overlap.SizeMatches(info, firstLevel) && isSamplerTexture)
                    {
                        texture.ChangeSize(info.Width, info.Height, info.DepthOrLayers);
                    }

                    break;
                }
            }

            // No match, create a new texture.
            if (texture == null)
            {
                texture = new Texture(_context, info, sizeInfo, scaleMode);

                // We need to synchronize before copying the old view data to the texture,
                // otherwise the copied data would be overwritten by a future synchronization.
                texture.SynchronizeMemory();

                for (int index = 0; index < overlapsCount; index++)
                {
                    Texture overlap = _textureOverlaps[index];

                    if (texture.IsViewCompatible(overlap.Info, overlap.Size, out int firstLayer, out int firstLevel))
                    {
                        TextureInfo overlapInfo = AdjustSizes(texture, overlap.Info, firstLevel);

                        TextureCreateInfo createInfo = GetCreateInfo(overlapInfo, _context.Capabilities);

                        if (texture.ScaleFactor != overlap.ScaleFactor)
                        {
                            // A bit tricky, our new texture may need to contain an existing texture that is upscaled, but isn't itself.
                            // In that case, we prefer the higher scale only if our format is render-target-like, otherwise we scale the view down before copy.

                            texture.PropagateScale(overlap);
                        }

                        ITexture newView = texture.HostTexture.CreateView(createInfo, firstLayer, firstLevel);

                        overlap.HostTexture.CopyTo(newView, 0, 0);

                        // Inherit modification from overlapping texture, do that before replacing
                        // the view since the replacement operation removes it from the list.
                        if (IsTextureModified(overlap))
                        {
                            CacheTextureModified(texture);
                        }

                        overlap.ReplaceView(texture, overlapInfo, newView, firstLayer, firstLevel);
                    }
                }

                // If the texture is a 3D texture, we need to additionally copy any slice
                // of the 3D texture to the newly created 3D texture.
                if (info.Target == Target.Texture3D)
                {
                    for (int index = 0; index < overlapsCount; index++)
                    {
                        Texture overlap = _textureOverlaps[index];

                        if (texture.IsViewCompatible(
                                overlap.Info,
                                overlap.Size,
                                isCopy: true,
                                out int firstLayer,
                                out int firstLevel))
                        {
                            overlap.BlacklistScale();

                            overlap.HostTexture.CopyTo(texture.HostTexture, firstLayer, firstLevel);

                            if (IsTextureModified(overlap))
                            {
                                CacheTextureModified(texture);
                            }
                        }
                    }
                }
            }

            // Sampler textures are managed by the texture pool, all other textures
            // are managed by the auto delete cache.
            if (!isSamplerTexture)
            {
                _cache.Add(texture);
                texture.Modified += CacheTextureModified;
                texture.Disposed += CacheTextureDisposed;
            }

            _textures.Add(texture);

            ShrinkOverlapsBufferIfNeeded();

            return(texture);
        }
Exemple #7
0
        /// <summary>
        /// Scale Texture.
        /// </summary>
        /// <param name="source"></param>
        /// <param name="scale"></param>
        /// <param name="textureScaleMode"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public static Texture2D ScaleTexture(Texture2D source, float scale, TextureScaleMode textureScaleMode)
        {
            int i;

            // Get All the source pixels
            var aSourceColor = source.GetPixels(0);
            var vSourceSize  = new Vector2(source.width, source.height);

            // Calculate New Size
            float xWidth  = Mathf.RoundToInt(source.width * scale);
            float xHeight = Mathf.RoundToInt(source.height * scale);

            // Make New
            var oNewTex = new Texture2D((int)xWidth, (int)xHeight, TextureFormat.RGBA32, false);

            // Make destination array
            var xLength = (int)xWidth * (int)xHeight;
            var aColor  = new Color[xLength];

            var vPixelSize = new Vector2(vSourceSize.x / xWidth, vSourceSize.y / xHeight);

            // Loop through destination pixels and process
            var vCenter = new Vector2();

            for (i = 0; i < xLength; i++)
            {
                // Figure out x&y
                var xX = i % xWidth;
                var xY = Mathf.Floor(i / xWidth);

                // Calculate Center
                vCenter.x = xX / xWidth * vSourceSize.x;
                vCenter.y = xY / xHeight * vSourceSize.y;

                // Do Based on mode
                // Nearest neighbour (testing)
                switch (textureScaleMode)
                {
                case TextureScaleMode.Nearest:
                {
                    // Nearest neighbour (testing)
                    vCenter.x = Mathf.Round(vCenter.x);
                    vCenter.y = Mathf.Round(vCenter.y);

                    // Calculate source index
                    var xSourceIndex = (int)(vCenter.y * vSourceSize.x + vCenter.x);

                    // Copy Pixel
                    aColor[i] = aSourceColor[xSourceIndex];
                    break;
                }

                case TextureScaleMode.Bilinear:
                {
                    // Get Ratios
                    var xRatioX = vCenter.x - Mathf.Floor(vCenter.x);
                    var xRatioY = vCenter.y - Mathf.Floor(vCenter.y);

                    // Get Pixel index's
                    var xIndexTl = (int)(Mathf.Floor(vCenter.y) * vSourceSize.x + Mathf.Floor(vCenter.x));
                    var xIndexTr = (int)(Mathf.Floor(vCenter.y) * vSourceSize.x + Mathf.Ceil(vCenter.x));
                    var xIndexBl = (int)(Mathf.Ceil(vCenter.y) * vSourceSize.x + Mathf.Floor(vCenter.x));
                    var xIndexBr = (int)(Mathf.Ceil(vCenter.y) * vSourceSize.x + Mathf.Ceil(vCenter.x));

                    // Calculate Color
                    aColor[i] = Color.Lerp(
                        Color.Lerp(aSourceColor[xIndexTl], aSourceColor[xIndexTr], xRatioX),
                        Color.Lerp(aSourceColor[xIndexBl], aSourceColor[xIndexBr], xRatioX),
                        xRatioY
                        );
                    break;
                }

                case TextureScaleMode.Average:
                {
                    // Calculate grid around point
                    var xXFrom = (int)Mathf.Max(Mathf.Floor(vCenter.x - vPixelSize.x * 0.5f), 0);
                    var xXTo   = (int)Mathf.Min(Mathf.Ceil(vCenter.x + vPixelSize.x * 0.5f), vSourceSize.x);
                    var xYFrom = (int)Mathf.Max(Mathf.Floor(vCenter.y - vPixelSize.y * 0.5f), 0);
                    var xYTo   = (int)Mathf.Min(Mathf.Ceil(vCenter.y + vPixelSize.y * 0.5f), vSourceSize.y);

                    // Loop and accumulate
                    var   oColorTemp = new Color();
                    float xGridCount = 0;
                    for (var iy = xYFrom; iy < xYTo; iy++)
                    {
                        for (var ix = xXFrom; ix < xXTo; ix++)
                        {
                            // Get Color
                            oColorTemp += aSourceColor[(int)(iy * vSourceSize.x + ix)];

                            // Sum
                            xGridCount++;
                        }
                    }

                    // Average Color
                    aColor[i] = oColorTemp / xGridCount;
                    break;
                }

                default:
                    throw new ArgumentOutOfRangeException(nameof(textureScaleMode), textureScaleMode, null);
                }
            }

            oNewTex.SetPixels(aColor);
            oNewTex.Apply();
            return(oNewTex);
        }
Exemple #8
0
        /// <summary>
        /// Tries to find an existing texture, or create a new one if not found.
        /// </summary>
        /// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
        /// <param name="flags">The texture search flags, defines texture comparison rules</param>
        /// <param name="info">Texture information of the texture to be found or created</param>
        /// <param name="layerSize">Size in bytes of a single texture layer</param>
        /// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
        /// <param name="range">Optional ranges of physical memory where the texture data is located</param>
        /// <returns>The texture</returns>
        public Texture FindOrCreateTexture(
            MemoryManager memoryManager,
            TextureSearchFlags flags,
            TextureInfo info,
            int layerSize    = 0,
            Size?sizeHint    = null,
            MultiRange?range = null)
        {
            bool isSamplerTexture = (flags & TextureSearchFlags.ForSampler) != 0;

            TextureScaleMode scaleMode = IsUpscaleCompatible(info, (flags & TextureSearchFlags.WithUpscale) != 0);

            ulong address;

            if (range != null)
            {
                address = range.Value.GetSubRange(0).Address;
            }
            else
            {
                address = memoryManager.Translate(info.GpuAddress);

                if (address == MemoryManager.PteUnmapped)
                {
                    return(null);
                }
            }

            int sameAddressOverlapsCount;

            lock (_textures)
            {
                // Try to find a perfect texture match, with the same address and parameters.
                sameAddressOverlapsCount = _textures.FindOverlaps(address, ref _textureOverlaps);
            }

            Texture texture = null;

            TextureMatchQuality bestQuality = TextureMatchQuality.NoMatch;

            for (int index = 0; index < sameAddressOverlapsCount; index++)
            {
                Texture overlap = _textureOverlaps[index];

                TextureMatchQuality matchQuality = overlap.IsExactMatch(info, flags);

                if (matchQuality != TextureMatchQuality.NoMatch)
                {
                    // If the parameters match, we need to make sure the texture is mapped to the same memory regions.
                    if (range != null)
                    {
                        // If a range of memory was supplied, just check if the ranges match.
                        if (!overlap.Range.Equals(range.Value))
                        {
                            continue;
                        }
                    }
                    else
                    {
                        // If no range was supplied, we can check if the GPU virtual address match. If they do,
                        // we know the textures are located at the same memory region.
                        // If they don't, it may still be mapped to the same physical region, so we
                        // do a more expensive check to tell if they are mapped into the same physical regions.
                        // If the GPU VA for the texture has ever been unmapped, then the range must be checked regardless.
                        if ((overlap.Info.GpuAddress != info.GpuAddress || overlap.ChangedMapping) &&
                            !memoryManager.CompareRange(overlap.Range, info.GpuAddress))
                        {
                            continue;
                        }
                    }
                }

                if (matchQuality == TextureMatchQuality.Perfect)
                {
                    texture = overlap;
                    break;
                }
                else if (matchQuality > bestQuality)
                {
                    texture     = overlap;
                    bestQuality = matchQuality;
                }
            }

            if (texture != null)
            {
                ChangeSizeIfNeeded(info, texture, isSamplerTexture, sizeHint);

                texture.SynchronizeMemory();

                return(texture);
            }
            else if (flags.HasFlag(TextureSearchFlags.NoCreate))
            {
                return(null);
            }

            // Calculate texture sizes, used to find all overlapping textures.
            SizeInfo sizeInfo = info.CalculateSizeInfo(layerSize);

            ulong size            = (ulong)sizeInfo.TotalSize;
            bool  partiallyMapped = false;

            if (range == null)
            {
                range = memoryManager.GetPhysicalRegions(info.GpuAddress, size);

                for (int i = 0; i < range.Value.Count; i++)
                {
                    if (range.Value.GetSubRange(i).Address == MemoryManager.PteUnmapped)
                    {
                        partiallyMapped = true;
                        break;
                    }
                }
            }

            // Find view compatible matches.
            int overlapsCount;

            lock (_textures)
            {
                overlapsCount = _textures.FindOverlaps(range.Value, ref _textureOverlaps);
            }

            if (_overlapInfo.Length != _textureOverlaps.Length)
            {
                Array.Resize(ref _overlapInfo, _textureOverlaps.Length);
            }

            // =============== Find Texture View of Existing Texture ===============

            int fullyCompatible = 0;

            // Evaluate compatibility of overlaps, add temporary references

            for (int index = 0; index < overlapsCount; index++)
            {
                Texture overlap = _textureOverlaps[index];
                TextureViewCompatibility overlapCompatibility = overlap.IsViewCompatible(info, range.Value, sizeInfo.LayerSize, _context.Capabilities, out int firstLayer, out int firstLevel);

                if (overlapCompatibility == TextureViewCompatibility.Full)
                {
                    if (overlap.IsView)
                    {
                        overlapCompatibility = TextureViewCompatibility.CopyOnly;
                    }
                    else
                    {
                        fullyCompatible++;
                    }
                }

                _overlapInfo[index] = new OverlapInfo(overlapCompatibility, firstLayer, firstLevel);
                overlap.IncrementReferenceCount();
            }

            // Search through the overlaps to find a compatible view and establish any copy dependencies.

            for (int index = 0; index < overlapsCount; index++)
            {
                Texture     overlap = _textureOverlaps[index];
                OverlapInfo oInfo   = _overlapInfo[index];

                if (oInfo.Compatibility == TextureViewCompatibility.Full)
                {
                    TextureInfo adjInfo = AdjustSizes(overlap, info, oInfo.FirstLevel);

                    if (!isSamplerTexture)
                    {
                        info = adjInfo;
                    }

                    texture = overlap.CreateView(adjInfo, sizeInfo, range.Value, oInfo.FirstLayer, oInfo.FirstLevel);

                    ChangeSizeIfNeeded(info, texture, isSamplerTexture, sizeHint);

                    texture.SynchronizeMemory();
                    break;
                }
                else if (oInfo.Compatibility == TextureViewCompatibility.CopyOnly && fullyCompatible == 0)
                {
                    // Only copy compatible. If there's another choice for a FULLY compatible texture, choose that instead.

                    texture = new Texture(_context, _physicalMemory, info, sizeInfo, range.Value, scaleMode);

                    texture.InitializeGroup(true, true, new List <TextureIncompatibleOverlap>());
                    texture.InitializeData(false, false);

                    overlap.SynchronizeMemory();
                    overlap.CreateCopyDependency(texture, oInfo.FirstLayer, oInfo.FirstLevel, true);
                    break;
                }
            }

            if (texture != null)
            {
                // This texture could be a view of multiple parent textures with different storages, even if it is a view.
                // When a texture is created, make sure all possible dependencies to other textures are created as copies.
                // (even if it could be fulfilled without a copy)

                for (int index = 0; index < overlapsCount; index++)
                {
                    Texture     overlap = _textureOverlaps[index];
                    OverlapInfo oInfo   = _overlapInfo[index];

                    if (oInfo.Compatibility <= TextureViewCompatibility.LayoutIncompatible)
                    {
                        if (!overlap.IsView && texture.DataOverlaps(overlap, oInfo.Compatibility))
                        {
                            texture.Group.RegisterIncompatibleOverlap(new TextureIncompatibleOverlap(overlap.Group, oInfo.Compatibility), true);
                        }
                    }
                    else if (overlap.Group != texture.Group)
                    {
                        overlap.SynchronizeMemory();
                        overlap.CreateCopyDependency(texture, oInfo.FirstLayer, oInfo.FirstLevel, true);
                    }
                }

                texture.SynchronizeMemory();
            }

            // =============== Create a New Texture ===============

            // No match, create a new texture.
            if (texture == null)
            {
                texture = new Texture(_context, _physicalMemory, info, sizeInfo, range.Value, scaleMode);

                // Step 1: Find textures that are view compatible with the new texture.
                // Any textures that are incompatible will contain garbage data, so they should be removed where possible.

                int viewCompatible = 0;
                fullyCompatible = 0;
                bool setData = isSamplerTexture || overlapsCount == 0 || flags.HasFlag(TextureSearchFlags.ForCopy);

                bool hasLayerViews = false;
                bool hasMipViews   = false;

                var incompatibleOverlaps = new List <TextureIncompatibleOverlap>();

                for (int index = 0; index < overlapsCount; index++)
                {
                    Texture overlap        = _textureOverlaps[index];
                    bool    overlapInCache = overlap.CacheNode != null;

                    TextureViewCompatibility compatibility = texture.IsViewCompatible(overlap.Info, overlap.Range, overlap.LayerSize, _context.Capabilities, out int firstLayer, out int firstLevel);

                    if (overlap.IsView && compatibility == TextureViewCompatibility.Full)
                    {
                        compatibility = TextureViewCompatibility.CopyOnly;
                    }

                    if (compatibility > TextureViewCompatibility.LayoutIncompatible)
                    {
                        _overlapInfo[viewCompatible]     = new OverlapInfo(compatibility, firstLayer, firstLevel);
                        _textureOverlaps[index]          = _textureOverlaps[viewCompatible];
                        _textureOverlaps[viewCompatible] = overlap;

                        if (compatibility == TextureViewCompatibility.Full)
                        {
                            if (viewCompatible != fullyCompatible)
                            {
                                // Swap overlaps so that the fully compatible views have priority.

                                _overlapInfo[viewCompatible]     = _overlapInfo[fullyCompatible];
                                _textureOverlaps[viewCompatible] = _textureOverlaps[fullyCompatible];

                                _overlapInfo[fullyCompatible]     = new OverlapInfo(compatibility, firstLayer, firstLevel);
                                _textureOverlaps[fullyCompatible] = overlap;
                            }

                            fullyCompatible++;
                        }

                        viewCompatible++;

                        hasLayerViews |= overlap.Info.GetSlices() < texture.Info.GetSlices();
                        hasMipViews   |= overlap.Info.Levels < texture.Info.Levels;
                    }
                    else
                    {
                        bool dataOverlaps = texture.DataOverlaps(overlap, compatibility);

                        if (!overlap.IsView && dataOverlaps && !incompatibleOverlaps.Exists(incompatible => incompatible.Group == overlap.Group))
                        {
                            incompatibleOverlaps.Add(new TextureIncompatibleOverlap(overlap.Group, compatibility));
                        }

                        bool removeOverlap;
                        bool modified = overlap.CheckModified(false);

                        if (overlapInCache || !setData)
                        {
                            if (!dataOverlaps)
                            {
                                // Allow textures to overlap if their data does not actually overlap.
                                // This typically happens when mip level subranges of a layered texture are used. (each texture fills the gaps of the others)
                                continue;
                            }

                            // The overlap texture is going to contain garbage data after we draw, or is generally incompatible.
                            // The texture group will obtain copy dependencies for any subresources that are compatible between the two textures,
                            // but sometimes its data must be flushed regardless.

                            // If the texture was modified since its last use, then that data is probably meant to go into this texture.
                            // If the data has been modified by the CPU, then it also shouldn't be flushed.

                            bool flush = overlapInCache && !modified && overlap.AlwaysFlushOnOverlap;

                            setData |= modified || flush;

                            if (overlapInCache)
                            {
                                _cache.Remove(overlap, flush);
                            }

                            removeOverlap = modified;
                        }
                        else
                        {
                            // If an incompatible overlapping texture has been modified, then it's data is likely destined for this texture,
                            // and the overlapped texture will contain garbage. In this case, it should be removed to save memory.
                            removeOverlap = modified;
                        }

                        if (removeOverlap && overlap.Info.Target != Target.TextureBuffer)
                        {
                            overlap.RemoveFromPools(false);
                        }
                    }
                }

                texture.InitializeGroup(hasLayerViews, hasMipViews, incompatibleOverlaps);

                // We need to synchronize before copying the old view data to the texture,
                // otherwise the copied data would be overwritten by a future synchronization.
                texture.InitializeData(false, setData);

                texture.Group.InitializeOverlaps();

                for (int index = 0; index < viewCompatible; index++)
                {
                    Texture overlap = _textureOverlaps[index];

                    OverlapInfo oInfo = _overlapInfo[index];

                    if (overlap.Group == texture.Group)
                    {
                        // If the texture group is equal, then this texture (or its parent) is already a view.
                        continue;
                    }

                    TextureInfo overlapInfo = AdjustSizes(texture, overlap.Info, oInfo.FirstLevel);

                    if (texture.ScaleFactor != overlap.ScaleFactor)
                    {
                        // A bit tricky, our new texture may need to contain an existing texture that is upscaled, but isn't itself.
                        // In that case, we prefer the higher scale only if our format is render-target-like, otherwise we scale the view down before copy.

                        texture.PropagateScale(overlap);
                    }

                    if (oInfo.Compatibility != TextureViewCompatibility.Full)
                    {
                        // Copy only compatibility, or target texture is already a view.

                        overlap.SynchronizeMemory();
                        texture.CreateCopyDependency(overlap, oInfo.FirstLayer, oInfo.FirstLevel, false);
                    }
                    else
                    {
                        TextureCreateInfo createInfo = GetCreateInfo(overlapInfo, _context.Capabilities, overlap.ScaleFactor);

                        ITexture newView = texture.HostTexture.CreateView(createInfo, oInfo.FirstLayer, oInfo.FirstLevel);

                        overlap.SynchronizeMemory();

                        overlap.HostTexture.CopyTo(newView, 0, 0);

                        overlap.ReplaceView(texture, overlapInfo, newView, oInfo.FirstLayer, oInfo.FirstLevel);
                    }
                }

                texture.SynchronizeMemory();
            }

            // Sampler textures are managed by the texture pool, all other textures
            // are managed by the auto delete cache.
            if (!isSamplerTexture)
            {
                _cache.Add(texture);
            }

            lock (_textures)
            {
                _textures.Add(texture);
            }

            if (partiallyMapped)
            {
                lock (_partiallyMappedTextures)
                {
                    _partiallyMappedTextures.Add(texture);
                }
            }

            ShrinkOverlapsBufferIfNeeded();

            for (int i = 0; i < overlapsCount; i++)
            {
                _textureOverlaps[i].DecrementReferenceCount();
            }

            return(texture);
        }
Exemple #9
0
        public static Texture2D Scale2D(this Texture2D texture, int newWidth, int newHeight, TextureScaleMode scaleMode)
        {
            Color[] curColors = texture.GetPixels();
            Color[] newColors = new Color[newWidth * newHeight];

            switch (scaleMode)
            {
            case TextureScaleMode.Bilinear:
                newColors = BilinearScale(curColors, texture.width, texture.height, newWidth, newHeight);
                break;

            case TextureScaleMode.Point:
                newColors = PointScale(curColors, texture.width, texture.height, newWidth, newHeight);
                break;
            }

            texture.Resize(newWidth, newHeight);
            texture.SetPixels(newColors);
            texture.Apply();

            return(texture);
        }
Exemple #10
0
        /// <summary>
        /// Tries to find an existing texture, or create a new one if not found.
        /// </summary>
        /// <param name="flags">The texture search flags, defines texture comparison rules</param>
        /// <param name="info">Texture information of the texture to be found or created</param>
        /// <param name="layerSize">Size in bytes of a single texture layer</param>
        /// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
        /// <param name="range">Optional ranges of physical memory where the texture data is located</param>
        /// <returns>The texture</returns>
        public Texture FindOrCreateTexture(TextureSearchFlags flags, TextureInfo info, int layerSize = 0, Size?sizeHint = null, MultiRange?range = null)
        {
            bool isSamplerTexture = (flags & TextureSearchFlags.ForSampler) != 0;

            bool isScalable = IsUpscaleCompatible(info);

            TextureScaleMode scaleMode = TextureScaleMode.Blacklisted;

            if (isScalable)
            {
                scaleMode = (flags & TextureSearchFlags.WithUpscale) != 0 ? TextureScaleMode.Scaled : TextureScaleMode.Eligible;
            }

            ulong address;

            if (range != null)
            {
                address = range.Value.GetSubRange(0).Address;
            }
            else
            {
                address = _context.MemoryManager.Translate(info.GpuAddress);

                if (address == MemoryManager.PteUnmapped)
                {
                    return(null);
                }
            }

            int sameAddressOverlapsCount;

            lock (_textures)
            {
                // Try to find a perfect texture match, with the same address and parameters.
                sameAddressOverlapsCount = _textures.FindOverlaps(address, ref _textureOverlaps);
            }

            Texture texture = null;

            TextureMatchQuality bestQuality = TextureMatchQuality.NoMatch;

            for (int index = 0; index < sameAddressOverlapsCount; index++)
            {
                Texture overlap = _textureOverlaps[index];

                bool rangeMatches = range != null?overlap.Range.Equals(range.Value) : overlap.Info.GpuAddress == info.GpuAddress;

                if (!rangeMatches)
                {
                    continue;
                }

                TextureMatchQuality matchQuality = overlap.IsExactMatch(info, flags);

                if (matchQuality == TextureMatchQuality.Perfect)
                {
                    texture = overlap;
                    break;
                }
                else if (matchQuality > bestQuality)
                {
                    texture     = overlap;
                    bestQuality = matchQuality;
                }
            }

            if (texture != null)
            {
                if (!isSamplerTexture)
                {
                    // If not a sampler texture, it is managed by the auto delete
                    // cache, ensure that it is on the "top" of the list to avoid
                    // deletion.
                    _cache.Lift(texture);
                }

                ChangeSizeIfNeeded(info, texture, isSamplerTexture, sizeHint);

                texture.SynchronizeMemory();

                return(texture);
            }

            // Calculate texture sizes, used to find all overlapping textures.
            SizeInfo sizeInfo = info.CalculateSizeInfo(layerSize);

            ulong size = (ulong)sizeInfo.TotalSize;

            if (range == null)
            {
                range = _context.MemoryManager.GetPhysicalRegions(info.GpuAddress, size);
            }

            // Find view compatible matches.
            int overlapsCount;

            lock (_textures)
            {
                overlapsCount = _textures.FindOverlaps(range.Value, ref _textureOverlaps);
            }

            for (int index = 0; index < overlapsCount; index++)
            {
                Texture overlap = _textureOverlaps[index];
                TextureViewCompatibility overlapCompatibility = overlap.IsViewCompatible(info, range.Value, out int firstLayer, out int firstLevel);

                if (overlapCompatibility == TextureViewCompatibility.Full)
                {
                    TextureInfo oInfo = AdjustSizes(overlap, info, firstLevel);

                    if (!isSamplerTexture)
                    {
                        info = oInfo;
                    }

                    texture = overlap.CreateView(oInfo, sizeInfo, range.Value, firstLayer, firstLevel);

                    if (overlap.IsModified)
                    {
                        texture.SignalModified();
                    }

                    ChangeSizeIfNeeded(info, texture, isSamplerTexture, sizeHint);

                    break;
                }
                else if (overlapCompatibility == TextureViewCompatibility.CopyOnly)
                {
                    // TODO: Copy rules for targets created after the container texture. See below.
                    overlap.DisableMemoryTracking();
                }
            }

            // No match, create a new texture.
            if (texture == null)
            {
                texture = new Texture(_context, info, sizeInfo, range.Value, scaleMode);

                // Step 1: Find textures that are view compatible with the new texture.
                // Any textures that are incompatible will contain garbage data, so they should be removed where possible.

                int  viewCompatible = 0;
                bool setData        = isSamplerTexture || overlapsCount == 0 || flags.HasFlag(TextureSearchFlags.ForCopy);

                for (int index = 0; index < overlapsCount; index++)
                {
                    Texture overlap        = _textureOverlaps[index];
                    bool    overlapInCache = overlap.CacheNode != null;

                    TextureViewCompatibility compatibility = texture.IsViewCompatible(overlap.Info, overlap.Range, out int firstLayer, out int firstLevel);

                    if (compatibility != TextureViewCompatibility.Incompatible)
                    {
                        if (_overlapInfo.Length != _textureOverlaps.Length)
                        {
                            Array.Resize(ref _overlapInfo, _textureOverlaps.Length);
                        }

                        _overlapInfo[viewCompatible]       = new OverlapInfo(compatibility, firstLayer, firstLevel);
                        _textureOverlaps[viewCompatible++] = overlap;
                    }
                    else if (overlapInCache || !setData)
                    {
                        if (info.GobBlocksInZ > 1 && info.GobBlocksInZ == overlap.Info.GobBlocksInZ)
                        {
                            // Allow overlapping slices of 3D textures. Could be improved in future by making sure the textures don't overlap.
                            continue;
                        }

                        // The overlap texture is going to contain garbage data after we draw, or is generally incompatible.
                        // If the texture cannot be entirely contained in the new address space, and one of its view children is compatible with us,
                        // it must be flushed before removal, so that the data is not lost.

                        // If the texture was modified since its last use, then that data is probably meant to go into this texture.
                        // If the data has been modified by the CPU, then it also shouldn't be flushed.
                        bool modified = overlap.ConsumeModified();

                        bool flush = overlapInCache && !modified && !texture.Range.Contains(overlap.Range) && overlap.HasViewCompatibleChild(texture);

                        setData |= modified || flush;

                        if (overlapInCache)
                        {
                            _cache.Remove(overlap, flush);
                        }
                    }
                }

                // We need to synchronize before copying the old view data to the texture,
                // otherwise the copied data would be overwritten by a future synchronization.
                texture.InitializeData(false, setData);

                for (int index = 0; index < viewCompatible; index++)
                {
                    Texture     overlap = _textureOverlaps[index];
                    OverlapInfo oInfo   = _overlapInfo[index];

                    if (oInfo.Compatibility != TextureViewCompatibility.Full)
                    {
                        continue; // Copy only compatibilty.
                    }

                    TextureInfo overlapInfo = AdjustSizes(texture, overlap.Info, oInfo.FirstLevel);

                    TextureCreateInfo createInfo = GetCreateInfo(overlapInfo, _context.Capabilities, overlap.ScaleFactor);

                    if (texture.ScaleFactor != overlap.ScaleFactor)
                    {
                        // A bit tricky, our new texture may need to contain an existing texture that is upscaled, but isn't itself.
                        // In that case, we prefer the higher scale only if our format is render-target-like, otherwise we scale the view down before copy.

                        texture.PropagateScale(overlap);
                    }

                    ITexture newView = texture.HostTexture.CreateView(createInfo, oInfo.FirstLayer, oInfo.FirstLevel);

                    overlap.HostTexture.CopyTo(newView, 0, 0);

                    // Inherit modification from overlapping texture, do that before replacing
                    // the view since the replacement operation removes it from the list.
                    if (overlap.IsModified)
                    {
                        texture.SignalModified();
                    }

                    overlap.ReplaceView(texture, overlapInfo, newView, oInfo.FirstLayer, oInfo.FirstLevel);
                }

                // If the texture is a 3D texture, we need to additionally copy any slice
                // of the 3D texture to the newly created 3D texture.
                if (info.Target == Target.Texture3D && viewCompatible > 0)
                {
                    // TODO: This copy can currently only happen when the 3D texture is created.
                    // If a game clears and redraws the slices, we won't be able to copy the new data to the 3D texture.
                    // Disable tracking to try keep at least the original data in there for as long as possible.
                    texture.DisableMemoryTracking();

                    for (int index = 0; index < viewCompatible; index++)
                    {
                        Texture     overlap = _textureOverlaps[index];
                        OverlapInfo oInfo   = _overlapInfo[index];

                        if (oInfo.Compatibility != TextureViewCompatibility.Incompatible)
                        {
                            overlap.BlacklistScale();

                            overlap.HostTexture.CopyTo(texture.HostTexture, oInfo.FirstLayer, oInfo.FirstLevel);

                            if (overlap.IsModified)
                            {
                                texture.SignalModified();
                            }
                        }
                    }
                }
            }

            // Sampler textures are managed by the texture pool, all other textures
            // are managed by the auto delete cache.
            if (!isSamplerTexture)
            {
                _cache.Add(texture);
            }

            lock (_textures)
            {
                _textures.Add(texture);
            }

            ShrinkOverlapsBufferIfNeeded();

            return(texture);
        }
Exemple #11
0
    /// <summary>
    /// Draws a texture.
    /// </summary>
    /// <param name="texture">The texture to draw.</param>
    /// <param name="position">The position where the texture will be drawn.</param>
    /// <param name="color">The color to multiply with the colors of the texture. If unspecified the colors of the texture will be unchanged.</param>
    /// <param name="size">The destination size of the texture. If unspecified the original texture size will be used.</param>
    /// <param name="rotation">The amount the texture will be rotated clockwise (in degrees). If unspecified the texture will not be rotated.</param>
    /// <param name="pivot">The offset from position to the pivot that the texture will be rotated about. If unspecified the center of the destination bounds will be used.</param>
    /// <param name="mirror">The mirroring to apply to the texture. If unspecified the texture will not be mirrored.</param>
    /// <param name="source">The source bounds of the texture to draw. If unspecified the entire texture will be drawn.</param>
    /// <param name="blendMode">The blend mode to use when drawing the texture. If unspecified the texture will be drawn using the standard alpha-based blend mode.</param>
    /// <param name="scaleMode">The scale mode to use when drawing the texture. If unspecified the texture will be linearly interpolated.</param>
    public static void DrawTexture(Texture texture, Vector2 position, Color?color = null, Vector2?size = null, float rotation = 0, Vector2?pivot = null, TextureMirror mirror = TextureMirror.None, Bounds2?source = null, TextureBlendMode blendMode = TextureBlendMode.Normal, TextureScaleMode scaleMode = TextureScaleMode.Linear)
    {
        DrawTextureSetup(texture.Handle, color, blendMode, scaleMode);

        SDL.SDL_Rect src;
        SDL.SDL_Rect dest;
        if (source.HasValue)
        {
            // Use the specified source coordinates:
            src.x  = (int)source.Value.Position.X;
            src.y  = (int)source.Value.Position.Y;
            src.w  = (int)source.Value.Size.X;
            src.h  = (int)source.Value.Size.Y;
            dest.x = (int)position.X;
            dest.y = (int)position.Y;
            dest.w = src.w;
            dest.h = src.h;
        }
        else
        {
            // Use the full texture as the source:
            src.x  = 0;
            src.y  = 0;
            src.w  = texture.Width;
            src.h  = texture.Height;
            dest.x = (int)position.X;
            dest.y = (int)position.Y;
            dest.w = texture.Width;
            dest.h = texture.Height;
        }

        // Apply the size override, if specified:
        if (size.HasValue)
        {
            dest.w = (int)size.Value.X;
            dest.h = (int)size.Value.Y;
        }

        // Apply the pivot override, if specified:
        SDL.SDL_Point center;
        if (pivot.HasValue)
        {
            center.x = (int)pivot.Value.X;
            center.y = (int)pivot.Value.Y;
        }
        else
        {
            center.x = dest.w / 2;
            center.y = dest.h / 2;
        }

        SDL.SDL_RenderCopyEx(Renderer, texture.Handle, ref src, ref dest, rotation, ref center, (SDL.SDL_RendererFlip)mirror);
    }
Exemple #12
0
    // ======================================================================================
    // Texture drawing
    // ======================================================================================

    private static void DrawTextureSetup(IntPtr textureHandle, Color?color, TextureBlendMode blendMode, TextureScaleMode scaleMode)
    {
        Color finalColor = color ?? Color.White;

        SDL.SDL_SetTextureColorMod(textureHandle, finalColor.R, finalColor.G, finalColor.B);
        SDL.SDL_SetTextureAlphaMod(textureHandle, finalColor.A);
        SDL.SDL_SetTextureBlendMode(textureHandle, (SDL.SDL_BlendMode)blendMode);
        SDL.SDL_SetTextureScaleMode(textureHandle, (SDL.SDL_ScaleMode)scaleMode);
    }
Exemple #13
0
    /// <summary>
    /// Draws a resizable texture.
    /// See the documentation for an explanation of how resizable textures work.
    /// </summary>
    /// <param name="texture">The resizable texture to draw.</param>
    /// <param name="bounds">The bounds that the texture should be resized to.</param>
    /// <param name="color">The color to multiply with the colors of the texture. If unspecified the colors of the texture will be unchanged.</param>
    /// <param name="blendMode">The blend mode to use when drawing the texture. If unspecified the texture will be drawn using the standard alpha-based blend mode.</param>
    /// <param name="scaleMode">The scale mode to use when drawing the texture. If unspecified the texture will be linearly interpolated.</param>
    public static void DrawResizableTexture(ResizableTexture texture, Bounds2 bounds, Color?color = null, TextureBlendMode blendMode = TextureBlendMode.Normal, TextureScaleMode scaleMode = TextureScaleMode.Linear)
    {
        DrawTextureSetup(texture.Handle, color, blendMode, scaleMode);

        /*
         *   0    bxmin     bxmax    txmax
         *   v    v             v    v
         *   +----|-------------|----+ < tymax
         *   | 1  |      5      | 2  |
         *   -----+-------------+----- < bymax
         *   |    |             |    |
         *   |    |             |    |
         *   | 6  |      9      | 7  |
         *   |    |             |    |
         *   |    |             |    |
         *   -----+-------------+----- < bymin
         *   | 3  |      8      | 4  |
         *   +----|-------------|----+ < 0
         */

        int bxmin = texture.LeftOffset;
        int bxmax = texture.RightOffset;
        int bymin = texture.TopOffset;
        int bymax = texture.BottomOffset;
        int txmax = texture.Width;
        int tymax = texture.Height;
        int px    = (int)bounds.Position.X;
        int py    = (int)bounds.Position.Y;

        // Don't let the overall size be so small that segment 9 has a negative size in either dimension:
        int sx = Math.Max((int)bounds.Size.X, txmax - bxmax + bxmin);
        int sy = Math.Max((int)bounds.Size.Y, tymax - bymax + bymin);

        // Draw each of the nine segments:
        DrawResizableTextureSegment(texture, 0, 0, bxmin, bymin, px, py, bxmin, bymin);
        DrawResizableTextureSegment(texture, bxmax, 0, txmax - bxmax, bymin, px + sx - (txmax - bxmax), py, txmax - bxmax, bymin);
        DrawResizableTextureSegment(texture, 0, bymax, bxmin, tymax - bymax, px, py + sy - (tymax - bymax), bxmin, tymax - bymax);
        DrawResizableTextureSegment(texture, bxmax, bymax, txmax - bxmax, tymax - bymax, px + sx - (txmax - bxmax), py + sy - (tymax - bymax), txmax - bxmax, tymax - bymax);
        DrawResizableTextureSegment(texture, bxmin, 0, bxmax - bxmin, bymin, px + bxmin, py, sx - bxmin - (txmax - bxmax), bymin);
        DrawResizableTextureSegment(texture, 0, bymin, bxmin, bymax - bymin, px, py + bymin, bxmin, sy - bymin - (tymax - bymax));
        DrawResizableTextureSegment(texture, bxmax, bymin, txmax - bxmax, bymax - bymin, px + sx - (txmax - bxmax), py + bymin, txmax - bxmax, sy - bymin - (tymax - bymax));
        DrawResizableTextureSegment(texture, bxmin, bymax, bxmax - bxmin, tymax - bymax, px + bxmin, py + sy - (tymax - bymax), sx - bxmin - (txmax - bxmax), tymax - bymax);
        DrawResizableTextureSegment(texture, bxmin, bymin, bxmax - bxmin, bymax - bymin, px + bxmin, py + bymin, sx - bxmin - (txmax - bxmax), sy - bymin - (tymax - bymax));
    }