Example #1
0
 /// <summary>
 /// Determines if a given texture is eligible for upscaling from its info.
 /// </summary>
 /// <param name="info">The texture info to check</param>
 /// <returns>True if eligible</returns>
 public bool IsUpscaleCompatible(TextureInfo info)
 {
     return((info.Target == Target.Texture2D || info.Target == Target.Texture2DArray) && info.Levels == 1 && !info.FormatInfo.IsCompressed && UpscaleSafeMode(info));
 }
Example #2
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));
 }
Example #3
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);
            }

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

            ulong size = (ulong)sizeInfo.TotalSize;

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

            // 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

            for (int index = 0; index < overlapsCount; index++)
            {
                Texture overlap = _textureOverlaps[index];
                TextureViewCompatibility overlapCompatibility = overlap.IsViewCompatible(info, range.Value, sizeInfo.LayerSize, 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);
            }

            // 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);
                    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.Incompatible && 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;

                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, out int firstLayer, out int firstLevel);

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

                    if (compatibility != TextureViewCompatibility.Incompatible)
                    {
                        if (compatibility == TextureViewCompatibility.Full)
                        {
                            if (viewCompatible == fullyCompatible)
                            {
                                _overlapInfo[viewCompatible]       = new OverlapInfo(compatibility, firstLayer, firstLevel);
                                _textureOverlaps[viewCompatible++] = overlap;
                            }
                            else
                            {
                                // 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++;
                        }
                        else
                        {
                            _overlapInfo[viewCompatible]       = new OverlapInfo(compatibility, firstLayer, firstLevel);
                            _textureOverlaps[viewCompatible++] = overlap;
                        }

                        hasLayerViews |= overlap.Info.GetSlices() < texture.Info.GetSlices();
                        hasMipViews   |= overlap.Info.Levels < texture.Info.Levels;
                    }
                    else
                    {
                        bool removeOverlap;
                        bool modified = overlap.CheckModified(false);

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

                            if (!texture.DataOverlaps(overlap))
                            {
                                // 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.
                            // 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 viewCompatibleChild = overlap.HasViewCompatibleChild(texture);

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

                            setData |= modified || flush;

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

                            removeOverlap = modified && !viewCompatibleChild;
                        }
                        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);

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

            ShrinkOverlapsBufferIfNeeded();

            return(texture);
        }
Example #4
0
 /// <summary>
 /// Checks if the texture sizes of the supplied texture information matches the given level of
 /// this texture.
 /// </summary>
 /// <param name="info">Texture information to compare with</param>
 /// <param name="level">Mipmap level of this texture to compare with</param>
 /// <returns>True if the size matches with the level, false otherwise</returns>
 public bool SizeMatches(TextureInfo info, int level)
 {
     return(Math.Max(1, Info.Width >> level) == info.Width &&
            Math.Max(1, Info.Height >> level) == info.Height &&
            Math.Max(1, Info.GetDepth() >> level) == info.GetDepth());
 }
Example #5
0
 /// <summary>
 /// Check if the texture target and samples count (for multisampled textures) matches.
 /// </summary>
 /// <param name="info">Texture information to compare with</param>
 /// <returns>True if the texture target and samples count matches, false otherwise</returns>
 private bool TargetAndSamplesCompatible(TextureInfo info)
 {
     return(Info.Target == info.Target &&
            Info.SamplesInX == info.SamplesInX &&
            Info.SamplesInY == info.SamplesInY);
 }
Example #6
0
        /// <summary>
        /// Adjusts the size of the texture information for a given mipmap level,
        /// based on the size of a parent texture.
        /// </summary>
        /// <param name="parent">The parent texture</param>
        /// <param name="info">The texture information to be adjusted</param>
        /// <param name="firstLevel">The first level of the texture view</param>
        /// <returns>The adjusted texture information with the new size</returns>
        private static TextureInfo AdjustSizes(Texture parent, TextureInfo info, int firstLevel)
        {
            // When the texture is used as view of another texture, we must
            // ensure that the sizes are valid, otherwise data uploads would fail
            // (and the size wouldn't match the real size used on the host API).
            // Given a parent texture from where the view is created, we have the
            // following rules:
            // - The view size must be equal to the parent size, divided by (2 ^ l),
            // where l is the first mipmap level of the view. The division result must
            // be rounded down, and the result must be clamped to 1.
            // - If the parent format is compressed, and the view format isn't, the
            // view size is calculated as above, but the width and height of the
            // view must be also divided by the compressed format block width and height.
            // - If the parent format is not compressed, and the view is, the view
            // size is calculated as described on the first point, but the width and height
            // of the view must be also multiplied by the block width and height.
            int width  = Math.Max(1, parent.Info.Width >> firstLevel);
            int height = Math.Max(1, parent.Info.Height >> firstLevel);

            if (parent.Info.FormatInfo.IsCompressed && !info.FormatInfo.IsCompressed)
            {
                width  = BitUtils.DivRoundUp(width, parent.Info.FormatInfo.BlockWidth);
                height = BitUtils.DivRoundUp(height, parent.Info.FormatInfo.BlockHeight);
            }
            else if (!parent.Info.FormatInfo.IsCompressed && info.FormatInfo.IsCompressed)
            {
                width  *= info.FormatInfo.BlockWidth;
                height *= info.FormatInfo.BlockHeight;
            }

            int depthOrLayers;

            if (info.Target == Target.Texture3D)
            {
                depthOrLayers = Math.Max(1, parent.Info.DepthOrLayers >> firstLevel);
            }
            else
            {
                depthOrLayers = info.DepthOrLayers;
            }

            return(new TextureInfo(
                       info.Address,
                       width,
                       height,
                       depthOrLayers,
                       info.Levels,
                       info.SamplesInX,
                       info.SamplesInY,
                       info.Stride,
                       info.IsLinear,
                       info.GobBlocksInY,
                       info.GobBlocksInZ,
                       info.GobBlocksInTileX,
                       info.Target,
                       info.FormatInfo,
                       info.DepthStencilMode,
                       info.SwizzleR,
                       info.SwizzleG,
                       info.SwizzleB,
                       info.SwizzleA));
        }
Example #7
0
 /// <summary>
 /// Checks if the texture sizes of the supplied texture informations match.
 /// </summary>
 /// <param name="lhs">Texture information to compare</param>
 /// <param name="rhs">Texture information to compare with</param>
 /// <returns>True if the size matches, false otherwise</returns>
 public static bool SizeMatches(TextureInfo lhs, TextureInfo rhs)
 {
     return(SizeMatches(lhs, rhs, alignSizes: false));
 }
Example #8
0
        /// <summary>
        /// Gets texture information from a texture descriptor.
        /// </summary>
        /// <param name="descriptor">The texture descriptor</param>
        /// <param name="layerSize">Layer size for textures using a sub-range of mipmap levels, otherwise 0</param>
        /// <returns>The texture information</returns>
        private TextureInfo GetInfo(TextureDescriptor descriptor, out int layerSize)
        {
            int depthOrLayers = descriptor.UnpackDepth();
            int levels        = descriptor.UnpackLevels();

            TextureMsaaMode msaaMode = descriptor.UnpackTextureMsaaMode();

            int samplesInX = msaaMode.SamplesInX();
            int samplesInY = msaaMode.SamplesInY();

            int stride = descriptor.UnpackStride();

            TextureDescriptorType descriptorType = descriptor.UnpackTextureDescriptorType();

            bool isLinear = descriptorType == TextureDescriptorType.Linear;

            Target target = descriptor.UnpackTextureTarget().Convert((samplesInX | samplesInY) != 1);

            int width  = target == Target.TextureBuffer ? descriptor.UnpackBufferTextureWidth() : descriptor.UnpackWidth();
            int height = descriptor.UnpackHeight();

            // We use 2D targets for 1D textures as that makes texture cache
            // management easier. We don't know the target for render target
            // and copies, so those would normally use 2D targets, which are
            // not compatible with 1D targets. By doing that we also allow those
            // to match when looking for compatible textures on the cache.
            if (target == Target.Texture1D)
            {
                target = Target.Texture2D;
                height = 1;
            }
            else if (target == Target.Texture1DArray)
            {
                target = Target.Texture2DArray;
                height = 1;
            }

            uint format = descriptor.UnpackFormat();
            bool srgb   = descriptor.UnpackSrgb();

            ulong gpuVa = descriptor.UnpackAddress();

            if (!FormatTable.TryGetTextureFormat(format, srgb, out FormatInfo formatInfo))
            {
                if (gpuVa != 0 && (int)format > 0)
                {
                    Logger.Error?.Print(LogClass.Gpu, $"Invalid texture format 0x{format:X} (sRGB: {srgb}).");
                }

                formatInfo = FormatInfo.Default;
            }

            int gobBlocksInY = descriptor.UnpackGobBlocksInY();
            int gobBlocksInZ = descriptor.UnpackGobBlocksInZ();

            int gobBlocksInTileX = descriptor.UnpackGobBlocksInTileX();

            layerSize = 0;

            int minLod = descriptor.UnpackBaseLevel();
            int maxLod = descriptor.UnpackMaxLevelInclusive();

            // Linear textures don't support mipmaps, so we don't handle this case here.
            if ((minLod != 0 || maxLod + 1 != levels) && target != Target.TextureBuffer && !isLinear)
            {
                int depth  = TextureInfo.GetDepth(target, depthOrLayers);
                int layers = TextureInfo.GetLayers(target, depthOrLayers);

                SizeInfo sizeInfo = SizeCalculator.GetBlockLinearTextureSize(
                    width,
                    height,
                    depth,
                    levels,
                    layers,
                    formatInfo.BlockWidth,
                    formatInfo.BlockHeight,
                    formatInfo.BytesPerPixel,
                    gobBlocksInY,
                    gobBlocksInZ,
                    gobBlocksInTileX);

                layerSize = sizeInfo.LayerSize;

                if (minLod != 0 && minLod < levels)
                {
                    // If the base level is not zero, we additionally add the mip level offset
                    // to the address, this allows the texture manager to find the base level from the
                    // address if there is a overlapping texture on the cache that can contain the new texture.
                    gpuVa += (ulong)sizeInfo.GetMipOffset(minLod);

                    width  = Math.Max(1, width >> minLod);
                    height = Math.Max(1, height >> minLod);

                    if (target == Target.Texture3D)
                    {
                        depthOrLayers = Math.Max(1, depthOrLayers >> minLod);
                    }

                    (gobBlocksInY, gobBlocksInZ) = SizeCalculator.GetMipGobBlockSizes(height, depth, formatInfo.BlockHeight, gobBlocksInY, gobBlocksInZ);
                }

                levels = (maxLod - minLod) + 1;
            }

            SwizzleComponent swizzleR = descriptor.UnpackSwizzleR().Convert();
            SwizzleComponent swizzleG = descriptor.UnpackSwizzleG().Convert();
            SwizzleComponent swizzleB = descriptor.UnpackSwizzleB().Convert();
            SwizzleComponent swizzleA = descriptor.UnpackSwizzleA().Convert();

            DepthStencilMode depthStencilMode = GetDepthStencilMode(
                formatInfo.Format,
                swizzleR,
                swizzleG,
                swizzleB,
                swizzleA);

            if (formatInfo.Format.IsDepthOrStencil())
            {
                swizzleR = SwizzleComponent.Red;
                swizzleG = SwizzleComponent.Red;
                swizzleB = SwizzleComponent.Red;

                if (depthStencilMode == DepthStencilMode.Depth)
                {
                    swizzleA = SwizzleComponent.One;
                }
                else
                {
                    swizzleA = SwizzleComponent.Red;
                }
            }

            return(new TextureInfo(
                       gpuVa,
                       width,
                       height,
                       depthOrLayers,
                       levels,
                       samplesInX,
                       samplesInY,
                       stride,
                       isLinear,
                       gobBlocksInY,
                       gobBlocksInZ,
                       gobBlocksInTileX,
                       target,
                       formatInfo,
                       depthStencilMode,
                       swizzleR,
                       swizzleG,
                       swizzleB,
                       swizzleA));
        }
Example #9
0
        /// <summary>
        /// Gets the texture with the given ID.
        /// </summary>
        /// <param name="id">ID of the texture. This is effectively a zero-based index</param>
        /// <returns>The texture with the given ID</returns>
        public override Texture Get(int id)
        {
            if ((uint)id >= Items.Length)
            {
                return(null);
            }

            if (SequenceNumber != Context.SequenceNumber)
            {
                SequenceNumber = Context.SequenceNumber;

                SynchronizeMemory();
            }

            Texture texture = Items[id];

            if (texture == null)
            {
                TextureDescriptor descriptor = GetDescriptor(id);

                TextureInfo info = GetInfo(descriptor, out int layerSize);

                ProcessDereferenceQueue();

                texture = PhysicalMemory.TextureCache.FindOrCreateTexture(_channel.MemoryManager, TextureSearchFlags.ForSampler, info, layerSize);

                // If this happens, then the texture address is invalid, we can't add it to the cache.
                if (texture == null)
                {
                    return(null);
                }

                texture.IncrementReferenceCount(this, id);

                Items[id] = texture;

                DescriptorCache[id] = descriptor;
            }
            else
            {
                if (texture.ChangedSize)
                {
                    // Texture changed size at one point - it may be a different size than the sampler expects.
                    // This can be triggered when the size is changed by a size hint on copy or draw, but the texture has been sampled before.

                    TextureDescriptor descriptor = GetDescriptor(id);

                    int baseLevel = descriptor.UnpackBaseLevel();
                    int width     = Math.Max(1, descriptor.UnpackWidth() >> baseLevel);
                    int height    = Math.Max(1, descriptor.UnpackHeight() >> baseLevel);

                    if (texture.Info.Width != width || texture.Info.Height != height)
                    {
                        texture.ChangeSize(width, height, texture.Info.DepthOrLayers);
                    }
                }

                // Memory is automatically synchronized on texture creation.
                texture.SynchronizeMemory();
            }

            return(texture);
        }
Example #10
0
        /// <summary>
        /// Check if the target of the first texture view information is compatible with the target of the second texture view information.
        /// This follows the host API target compatibility rules.
        /// </summary>
        /// <param name="lhs">Texture information of the texture view</param
        /// <param name="rhs">Texture information of the texture view</param>
        /// <param name="caps">Host GPU capabilities</param>
        /// <returns>True if the targets are compatible, false otherwise</returns>
        public static TextureViewCompatibility ViewTargetCompatible(TextureInfo lhs, TextureInfo rhs, ref Capabilities caps)
        {
            bool result = false;

            switch (lhs.Target)
            {
            case Target.Texture1D:
            case Target.Texture1DArray:
                result = rhs.Target == Target.Texture1D ||
                         rhs.Target == Target.Texture1DArray;
                break;

            case Target.Texture2D:
                result = rhs.Target == Target.Texture2D ||
                         rhs.Target == Target.Texture2DArray;
                break;

            case Target.Texture2DArray:
                result = rhs.Target == Target.Texture2D ||
                         rhs.Target == Target.Texture2DArray;

                if (rhs.Target == Target.Cubemap || rhs.Target == Target.CubemapArray)
                {
                    return(caps.SupportsCubemapView ? TextureViewCompatibility.Full : TextureViewCompatibility.CopyOnly);
                }
                break;

            case Target.Cubemap:
            case Target.CubemapArray:
                result = rhs.Target == Target.Cubemap ||
                         rhs.Target == Target.CubemapArray;

                if (rhs.Target == Target.Texture2D || rhs.Target == Target.Texture2DArray)
                {
                    return(caps.SupportsCubemapView ? TextureViewCompatibility.Full : TextureViewCompatibility.CopyOnly);
                }
                break;

            case Target.Texture2DMultisample:
            case Target.Texture2DMultisampleArray:
                if (rhs.Target == Target.Texture2D || rhs.Target == Target.Texture2DArray)
                {
                    return(TextureViewCompatibility.CopyOnly);
                }

                result = rhs.Target == Target.Texture2DMultisample ||
                         rhs.Target == Target.Texture2DMultisampleArray;
                break;

            case Target.Texture3D:
                if (rhs.Target == Target.Texture2D)
                {
                    return(TextureViewCompatibility.CopyOnly);
                }

                result = rhs.Target == Target.Texture3D;
                break;
            }

            return(result ? TextureViewCompatibility.Full : TextureViewCompatibility.Incompatible);
        }
Example #11
0
 /// <summary>
 /// Check if the texture target and samples count (for multisampled textures) matches.
 /// </summary>
 /// <param name="first">Texture information to compare with</param>
 /// <param name="rhs">Texture information to compare with</param>
 /// <returns>True if the texture target and samples count matches, false otherwise</returns>
 public static bool TargetAndSamplesCompatible(TextureInfo lhs, TextureInfo rhs)
 {
     return(lhs.Target == rhs.Target &&
            lhs.SamplesInX == rhs.SamplesInX &&
            lhs.SamplesInY == rhs.SamplesInY);
 }
Example #12
0
        /// <summary>
        /// Converts a incompatible format to a host compatible format, or return the format directly
        /// if it is already host compatible.
        /// </summary>
        /// <remarks>
        /// This can be used to convert a incompatible compressed format to the decompressor
        /// output format.
        /// </remarks>
        /// <param name="info">Texture information</param>
        /// <param name="caps">Host GPU capabilities</param>
        /// <returns>A host compatible format</returns>
        public static FormatInfo ToHostCompatibleFormat(TextureInfo info, Capabilities caps)
        {
            if (!caps.SupportsAstcCompression)
            {
                if (info.FormatInfo.Format.IsAstcUnorm())
                {
                    return(GraphicsConfig.EnableTextureRecompression
                        ? new FormatInfo(Format.Bc7Unorm, 4, 4, 16, 4)
                        : new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4));
                }
                else if (info.FormatInfo.Format.IsAstcSrgb())
                {
                    return(GraphicsConfig.EnableTextureRecompression
                        ? new FormatInfo(Format.Bc7Srgb, 4, 4, 16, 4)
                        : new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4, 4));
                }
            }

            if (!caps.SupportsR4G4Format && info.FormatInfo.Format == Format.R4G4Unorm)
            {
                return(new FormatInfo(Format.R4G4B4A4Unorm, 1, 1, 2, 4));
            }

            if (!HostSupportsBcFormat(info.FormatInfo.Format, info.Target, caps))
            {
                // The host API does not this compressed format.
                // We assume software decompression will be done for those textures,
                // and so we adjust the format here to match the decompressor output.
                switch (info.FormatInfo.Format)
                {
                case Format.Bc1RgbaSrgb:
                case Format.Bc2Srgb:
                case Format.Bc3Srgb:
                case Format.Bc7Srgb:
                    return(new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4, 4));

                case Format.Bc1RgbaUnorm:
                case Format.Bc2Unorm:
                case Format.Bc3Unorm:
                case Format.Bc7Unorm:
                    return(new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4));

                case Format.Bc4Unorm:
                    return(new FormatInfo(Format.R8Unorm, 1, 1, 1, 1));

                case Format.Bc4Snorm:
                    return(new FormatInfo(Format.R8Snorm, 1, 1, 1, 1));

                case Format.Bc5Unorm:
                    return(new FormatInfo(Format.R8G8Unorm, 1, 1, 2, 2));

                case Format.Bc5Snorm:
                    return(new FormatInfo(Format.R8G8Snorm, 1, 1, 2, 2));

                case Format.Bc6HSfloat:
                case Format.Bc6HUfloat:
                    return(new FormatInfo(Format.R16G16B16A16Float, 1, 1, 8, 4));
                }
            }

            return(info.FormatInfo);
        }
Example #13
0
        /// <summary>
        /// Checks if a format is host incompatible.
        /// </summary>
        /// <remarks>
        /// Host incompatible formats can't be used directly, the texture data needs to be converted
        /// to a compatible format first.
        /// </remarks>
        /// <param name="info">Texture information</param>
        /// <param name="caps">Host GPU capabilities</param>
        /// <returns>True if the format is incompatible, false otherwise</returns>
        public static bool IsFormatHostIncompatible(TextureInfo info, Capabilities caps)
        {
            Format originalFormat = info.FormatInfo.Format;

            return(ToHostCompatibleFormat(info, caps).Format != originalFormat);
        }
Example #14
0
 /// <summary>
 /// Checks if the texture sizes of the supplied texture informations match the given level
 /// </summary>
 /// <param name="lhs">Texture information to compare</param>
 /// <param name="rhs">Texture information to compare with</param>
 /// <param name="level">Mipmap level of this texture to compare with</param>
 /// <returns>True if the size matches with the level, false otherwise</returns>
 public static bool SizeMatches(TextureInfo lhs, TextureInfo rhs, int level)
 {
     return(Math.Max(1, lhs.Width >> level) == rhs.Width &&
            Math.Max(1, lhs.Height >> level) == rhs.Height &&
            Math.Max(1, lhs.GetDepth() >> level) == rhs.GetDepth());
 }
Example #15
0
        /// <summary>
        /// Tries to find an existing texture, or create a new one if not found.
        /// </summary>
        /// <param name="colorState">Color buffer texture to find or create</param>
        /// <param name="samplesInX">Number of samples in the X direction, for MSAA</param>
        /// <param name="samplesInY">Number of samples in the Y direction, for MSAA</param>
        /// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
        /// <returns>The texture</returns>
        public Texture FindOrCreateTexture(RtColorState colorState, int samplesInX, int samplesInY, Size sizeHint)
        {
            ulong address = _context.MemoryManager.Translate(colorState.Address.Pack());

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

            bool isLinear = colorState.MemoryLayout.UnpackIsLinear();

            int gobBlocksInY = colorState.MemoryLayout.UnpackGobBlocksInY();
            int gobBlocksInZ = colorState.MemoryLayout.UnpackGobBlocksInZ();

            Target target;

            if (colorState.MemoryLayout.UnpackIsTarget3D())
            {
                target = Target.Texture3D;
            }
            else if ((samplesInX | samplesInY) != 1)
            {
                target = colorState.Depth > 1
                    ? Target.Texture2DMultisampleArray
                    : Target.Texture2DMultisample;
            }
            else
            {
                target = colorState.Depth > 1
                    ? Target.Texture2DArray
                    : Target.Texture2D;
            }

            FormatInfo formatInfo = colorState.Format.Convert();

            int width, stride;

            // For linear textures, the width value is actually the stride.
            // We can easily get the width by dividing the stride by the bpp,
            // since the stride is the total number of bytes occupied by a
            // line. The stride should also meet alignment constraints however,
            // so the width we get here is the aligned width.
            if (isLinear)
            {
                width  = colorState.WidthOrStride / formatInfo.BytesPerPixel;
                stride = colorState.WidthOrStride;
            }
            else
            {
                width  = colorState.WidthOrStride;
                stride = 0;
            }

            TextureInfo info = new TextureInfo(
                address,
                width,
                colorState.Height,
                colorState.Depth,
                1,
                samplesInX,
                samplesInY,
                stride,
                isLinear,
                gobBlocksInY,
                gobBlocksInZ,
                1,
                target,
                formatInfo);

            Texture texture = FindOrCreateTexture(info, TextureSearchFlags.WithUpscale, sizeHint);

            texture.SynchronizeMemory();

            return(texture);
        }
Example #16
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);
        }
Example #17
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>
        /// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
        /// <returns>The texture</returns>
        public Texture FindOrCreateTexture(TextureInfo info, TextureSearchFlags flags = TextureSearchFlags.None, Size?sizeHint = 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;
            }

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

                    ChangeSizeIfNeeded(info, overlap, isSamplerTexture, sizeHint);

                    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];
                TextureViewCompatibility overlapCompatibility = overlap.IsViewCompatible(info, size, 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, 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, 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.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, 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);
        }
Example #18
0
 /// <summary>
 /// Checks if the texture sizes of the supplied texture information matches this texture.
 /// </summary>
 /// <param name="info">Texture information to compare with</param>
 /// <returns>True if the size matches, false otherwise</returns>
 public bool SizeMatches(TextureInfo info)
 {
     return(SizeMatches(info, alignSizes: false));
 }
Example #19
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));
        }
Example #20
0
 /// <summary>
 /// Checks if the potential child texture fits within the level and layer bounds of the parent.
 /// </summary>
 /// <param name="parent">Texture information for the parent</param>
 /// <param name="child">Texture information for the child</param>
 /// <param name="layer">Base layer of the child texture</param>
 /// <param name="level">Base level of the child texture</param>
 /// <returns>Full compatiblity if the child's layer and level count fit within the parent, incompatible otherwise</returns>
 public static TextureViewCompatibility ViewSubImagesInBounds(TextureInfo parent, TextureInfo child, int layer, int level)
 {
     if (level + child.Levels <= parent.Levels &&
         layer + child.GetSlices() <= parent.GetSlices())
     {
         return(TextureViewCompatibility.Full);
     }
     else
     {
         return(TextureViewCompatibility.LayoutIncompatible);
     }
 }