Exemple #1
0
        /// <summary>
        /// Flushes the texture data, to be called from an external thread.
        /// The host backend must ensure that we have shared access to the resource from this thread.
        /// This is used when flushing from memory access handlers.
        /// </summary>
        public void ExternalFlush(ulong address, ulong size)
        {
            if (!IsModified || _memoryTracking == null)
            {
                return;
            }

            _context.Renderer.BackgroundContextAction(() =>
            {
                IsModified = false;
                if (TextureCompatibility.IsFormatHostIncompatible(Info, _context.Capabilities))
                {
                    return; // Flushing this format is not supported, as it may have been converted to another host format.
                }

                ITexture texture = HostTexture;
                if (ScaleFactor != 1f)
                {
                    // If needed, create a texture to flush back to host at 1x scale.
                    texture = _flushHostTexture = GetScaledHostTexture(1f, _flushHostTexture);
                }

                _context.MemoryManager.WriteUntracked(Info.GpuAddress, GetTextureDataFromGpu(false, texture));
            });
        }
Exemple #2
0
        /// <summary>
        /// Changes a texture's size to match the desired size for samplers,
        /// or increases a texture's size to fit the region indicated by a size hint.
        /// </summary>
        /// <param name="info">The desired texture info</param>
        /// <param name="texture">The texture to resize</param>
        /// <param name="isSamplerTexture">True if the texture will be used for a sampler, false otherwise</param>
        /// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
        private void ChangeSizeIfNeeded(TextureInfo info, Texture texture, bool isSamplerTexture, Size?sizeHint)
        {
            if (isSamplerTexture)
            {
                // 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.

                if (!TextureCompatibility.SizeMatches(texture.Info, info))
                {
                    texture.ChangeSize(info.Width, info.Height, info.DepthOrLayers);
                }
            }
            else if (sizeHint != null)
            {
                // A size hint indicates that data will be used within that range, at least.
                // If the texture is smaller than the size hint, it must be enlarged to meet it.
                // The maximum size is provided by the requested info, which generally has an aligned size.

                int width  = Math.Max(texture.Info.Width, Math.Min(sizeHint.Value.Width, info.Width));
                int height = Math.Max(texture.Info.Height, Math.Min(sizeHint.Value.Height, info.Height));

                if (texture.Info.Width != width || texture.Info.Height != height)
                {
                    texture.ChangeSize(width, height, info.DepthOrLayers);
                }
            }
        }
Exemple #3
0
        /// <summary>
        /// Flushes the texture data, to be called from an external thread.
        /// The host backend must ensure that we have shared access to the resource from this thread.
        /// This is used when flushing from memory access handlers.
        /// </summary>
        public void ExternalFlush(ulong address, ulong size)
        {
            if (!IsModified || _memoryTracking == null)
            {
                return;
            }

            _context.Renderer.BackgroundContextAction(() =>
            {
                IsModified = false;
                if (TextureCompatibility.IsFormatHostIncompatible(Info, _context.Capabilities))
                {
                    return; // Flushing this format is not supported, as it may have been converted to another host format.
                }

                if (Info.Target == Target.Texture2DMultisample ||
                    Info.Target == Target.Texture2DMultisampleArray)
                {
                    return; // Flushing multisample textures is not supported, the host does not allow getting their data.
                }

                ITexture texture = HostTexture;
                if (ScaleFactor != 1f)
                {
                    // If needed, create a texture to flush back to host at 1x scale.
                    texture = _flushHostTexture = GetScaledHostTexture(1f, _flushHostTexture);
                }

                _context.PhysicalMemory.WriteUntracked(Range, GetTextureDataFromGpu(false, texture));
            });
        }
Exemple #4
0
        /// <summary>
        /// Flushes the texture data.
        /// This causes the texture data to be written back to guest memory.
        /// If the texture was written by the GPU, this includes all modification made by the GPU
        /// up to this point.
        /// Be aware that this is an expensive operation, avoid calling it unless strictly needed.
        /// This may cause data corruption if the memory is already being used for something else on the CPU side.
        /// </summary>
        /// <param name="tracked">Whether or not the flush triggers write tracking. If it doesn't, the texture will not be blacklisted for scaling either.</param>
        public void Flush(bool tracked = true)
        {
            IsModified = false;
            if (TextureCompatibility.IsFormatHostIncompatible(Info, _context.Capabilities))
            {
                return; // Flushing this format is not supported, as it may have been converted to another host format.
            }

            if (tracked)
            {
                _context.MemoryManager.Write(Info.GpuAddress, GetTextureDataFromGpu(tracked));
            }
            else
            {
                _context.MemoryManager.WriteUntracked(Info.GpuAddress, GetTextureDataFromGpu(tracked));
            }
        }
Exemple #5
0
        /// <summary>
        /// This performs a strict comparison, used to check if this texture is equal to the one supplied.
        /// </summary>
        /// <param name="info">Texture information to compare against</param>
        /// <param name="flags">Comparison flags</param>
        /// <returns>A value indicating how well this texture matches the given info</returns>
        public TextureMatchQuality IsExactMatch(TextureInfo info, TextureSearchFlags flags)
        {
            TextureMatchQuality matchQuality = TextureCompatibility.FormatMatches(Info, info, (flags & TextureSearchFlags.ForSampler) != 0, (flags & TextureSearchFlags.ForCopy) != 0);

            if (matchQuality == TextureMatchQuality.NoMatch)
            {
                return(matchQuality);
            }

            if (!TextureCompatibility.LayoutMatches(Info, info))
            {
                return(TextureMatchQuality.NoMatch);
            }

            if (!TextureCompatibility.SizeMatches(Info, info, (flags & TextureSearchFlags.Strict) == 0))
            {
                return(TextureMatchQuality.NoMatch);
            }

            if ((flags & TextureSearchFlags.ForSampler) != 0 || (flags & TextureSearchFlags.Strict) != 0)
            {
                if (!TextureCompatibility.SamplerParamsMatches(Info, info))
                {
                    return(TextureMatchQuality.NoMatch);
                }
            }

            if ((flags & TextureSearchFlags.ForCopy) != 0)
            {
                bool msTargetCompatible = Info.Target == Target.Texture2DMultisample && info.Target == Target.Texture2D;

                if (!msTargetCompatible && !TextureCompatibility.TargetAndSamplesCompatible(Info, info))
                {
                    return(TextureMatchQuality.NoMatch);
                }
            }
            else if (!TextureCompatibility.TargetAndSamplesCompatible(Info, info))
            {
                return(TextureMatchQuality.NoMatch);
            }

            return(Info.Address == info.Address && Info.Levels == info.Levels ? matchQuality : TextureMatchQuality.NoMatch);
        }
Exemple #6
0
        /// <summary>
        /// This performs a strict comparison, used to check if this texture is equal to the one supplied.
        /// </summary>
        /// <param name="info">Texture information to compare against</param>
        /// <param name="flags">Comparison flags</param>
        /// <returns>True if the textures are strictly equal or similar, false otherwise</returns>
        public bool IsPerfectMatch(TextureInfo info, TextureSearchFlags flags)
        {
            if (!TextureCompatibility.FormatMatches(Info, info, (flags & TextureSearchFlags.ForSampler) != 0, (flags & TextureSearchFlags.ForCopy) != 0))
            {
                return(false);
            }

            if (!TextureCompatibility.LayoutMatches(Info, info))
            {
                return(false);
            }

            if (!TextureCompatibility.SizeMatches(Info, info, (flags & TextureSearchFlags.Strict) == 0))
            {
                return(false);
            }

            if ((flags & TextureSearchFlags.ForSampler) != 0 || (flags & TextureSearchFlags.Strict) != 0)
            {
                if (!TextureCompatibility.SamplerParamsMatches(Info, info))
                {
                    return(false);
                }
            }

            if ((flags & TextureSearchFlags.ForCopy) != 0)
            {
                bool msTargetCompatible = Info.Target == Target.Texture2DMultisample && info.Target == Target.Texture2D;

                if (!msTargetCompatible && !TextureCompatibility.TargetAndSamplesCompatible(Info, info))
                {
                    return(false);
                }
            }
            else if (!TextureCompatibility.TargetAndSamplesCompatible(Info, info))
            {
                return(false);
            }

            return(Info.Address == info.Address && Info.Levels == info.Levels);
        }
Exemple #7
0
        /// <summary>
        /// Check if it's possible to create a view, with the given parameters, from this texture.
        /// </summary>
        /// <param name="info">Texture view information</param>
        /// <param name="size">Texture view size</param>
        /// <param name="firstLayer">Texture view initial layer on this texture</param>
        /// <param name="firstLevel">Texture view first mipmap level on this texture</param>
        /// <returns>The level of compatiblilty a view with the given parameters created from this texture has</returns>
        public TextureViewCompatibility IsViewCompatible(
            TextureInfo info,
            ulong size,
            out int firstLayer,
            out int firstLevel)
        {
            // Out of range.
            if (info.Address < Address || info.Address + size > EndAddress)
            {
                firstLayer = 0;
                firstLevel = 0;

                return(TextureViewCompatibility.Incompatible);
            }

            int offset = (int)(info.Address - Address);

            if (!_sizeInfo.FindView(offset, (int)size, out firstLayer, out firstLevel))
            {
                return(TextureViewCompatibility.Incompatible);
            }

            if (!TextureCompatibility.ViewLayoutCompatible(Info, info, firstLevel))
            {
                return(TextureViewCompatibility.Incompatible);
            }

            if (!TextureCompatibility.ViewFormatCompatible(Info, info))
            {
                return(TextureViewCompatibility.Incompatible);
            }

            TextureViewCompatibility result = TextureViewCompatibility.Full;

            result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewSizeMatches(Info, info, firstLevel));
            result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info));

            return((Info.SamplesInX == info.SamplesInX &&
                    Info.SamplesInY == info.SamplesInY) ? result : TextureViewCompatibility.Incompatible);
        }
Exemple #8
0
        /// <summary>
        /// Check if it's possible to create a view, with the given parameters, from this texture.
        /// </summary>
        /// <param name="info">Texture view information</param>
        /// <param name="range">Texture view physical memory ranges</param>
        /// <param name="firstLayer">Texture view initial layer on this texture</param>
        /// <param name="firstLevel">Texture view first mipmap level on this texture</param>
        /// <returns>The level of compatiblilty a view with the given parameters created from this texture has</returns>
        public TextureViewCompatibility IsViewCompatible(TextureInfo info, MultiRange range, int layerSize, out int firstLayer, out int firstLevel)
        {
            int offset = Range.FindOffset(range);

            // Out of range.
            if (offset < 0)
            {
                firstLayer = 0;
                firstLevel = 0;

                return(TextureViewCompatibility.Incompatible);
            }

            if (!_sizeInfo.FindView(offset, out firstLayer, out firstLevel))
            {
                return(TextureViewCompatibility.Incompatible);
            }

            if (!TextureCompatibility.ViewLayoutCompatible(Info, info, firstLevel))
            {
                return(TextureViewCompatibility.Incompatible);
            }

            if (info.GetSlices() > 1 && LayerSize != layerSize)
            {
                return(TextureViewCompatibility.Incompatible);
            }

            TextureViewCompatibility result = TextureViewCompatibility.Full;

            result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewFormatCompatible(Info, info));
            result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewSizeMatches(Info, info, firstLevel));
            result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info));
            result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewSubImagesInBounds(Info, info, firstLayer, firstLevel));

            return((Info.SamplesInX == info.SamplesInX &&
                    Info.SamplesInY == info.SamplesInY) ? result : TextureViewCompatibility.Incompatible);
        }
Exemple #9
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 #10
0
        /// <summary>
        /// Gets a texture creation information from texture information.
        /// This can be used to create new host textures.
        /// </summary>
        /// <param name="info">Texture information</param>
        /// <param name="caps">GPU capabilities</param>
        /// <param name="scale">Texture scale factor, to be applied to the texture size</param>
        /// <returns>The texture creation information</returns>
        public static TextureCreateInfo GetCreateInfo(TextureInfo info, Capabilities caps, float scale)
        {
            FormatInfo formatInfo = TextureCompatibility.ToHostCompatibleFormat(info, caps);

            if (info.Target == Target.TextureBuffer)
            {
                // We assume that the host does not support signed normalized format
                // (as is the case with OpenGL), so we just use a unsigned format.
                // The shader will need the appropriate conversion code to compensate.
                switch (formatInfo.Format)
                {
                case Format.R8Snorm:
                    formatInfo = new FormatInfo(Format.R8Sint, 1, 1, 1, 1);
                    break;

                case Format.R16Snorm:
                    formatInfo = new FormatInfo(Format.R16Sint, 1, 1, 2, 1);
                    break;

                case Format.R8G8Snorm:
                    formatInfo = new FormatInfo(Format.R8G8Sint, 1, 1, 2, 2);
                    break;

                case Format.R16G16Snorm:
                    formatInfo = new FormatInfo(Format.R16G16Sint, 1, 1, 4, 2);
                    break;

                case Format.R8G8B8A8Snorm:
                    formatInfo = new FormatInfo(Format.R8G8B8A8Sint, 1, 1, 4, 4);
                    break;

                case Format.R16G16B16A16Snorm:
                    formatInfo = new FormatInfo(Format.R16G16B16A16Sint, 1, 1, 8, 4);
                    break;
                }
            }

            int width  = info.Width / info.SamplesInX;
            int height = info.Height / info.SamplesInY;

            int depth = info.GetDepth() * info.GetLayers();

            if (scale != 1f)
            {
                width  = (int)MathF.Ceiling(width * scale);
                height = (int)MathF.Ceiling(height * scale);
            }

            return(new TextureCreateInfo(
                       width,
                       height,
                       depth,
                       info.Levels,
                       info.Samples,
                       formatInfo.BlockWidth,
                       formatInfo.BlockHeight,
                       formatInfo.BytesPerPixel,
                       formatInfo.Format,
                       info.DepthStencilMode,
                       info.Target,
                       info.SwizzleR,
                       info.SwizzleG,
                       info.SwizzleB,
                       info.SwizzleA));
        }
Exemple #11
0
 /// <summary>
 /// Checks if the view format is compatible with this texture format.
 /// In general, the formats are considered compatible if the bytes per pixel values are equal,
 /// but there are more complex rules for some formats, like compressed or depth-stencil formats.
 /// This follows the host API copy compatibility rules.
 /// </summary>
 /// <param name="info">Texture information of the texture view</param>
 /// <returns>True if the formats are compatible, false otherwise</returns>
 private bool ViewFormatCompatible(TextureInfo info)
 {
     return(TextureCompatibility.FormatCompatible(Info.FormatInfo, info.FormatInfo));
 }
Exemple #12
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;
            }

            // 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 (!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 = _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 (!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);

                // 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);
        }