/// <summary>
        /// Updates the texture scale for a given texture or image.
        /// </summary>
        /// <param name="texture">Start GPU virtual address of the pool</param>
        /// <param name="binding">The related texture binding</param>
        /// <param name="index">The texture/image binding index</param>
        /// <param name="stage">The active shader stage</param>
        /// <returns>True if the given texture has become blacklisted, indicating that its host texture may have changed.</returns>
        private bool UpdateScale(Texture texture, TextureBindingInfo binding, int index, ShaderStage stage)
        {
            float result  = 1f;
            bool  changed = false;

            if ((binding.Flags & TextureUsageFlags.NeedsScaleValue) != 0 && texture != null)
            {
                switch (stage)
                {
                case ShaderStage.Fragment:
                    if ((binding.Flags & TextureUsageFlags.ResScaleUnsupported) != 0)
                    {
                        changed |= texture.ScaleMode != TextureScaleMode.Blacklisted;
                        texture.BlacklistScale();
                        break;
                    }

                    float scale = texture.ScaleFactor;

                    if (scale != 1)
                    {
                        Texture activeTarget = _channel.TextureManager.GetAnyRenderTarget();

                        if (activeTarget != null && activeTarget.Info.Width / (float)texture.Info.Width == activeTarget.Info.Height / (float)texture.Info.Height)
                        {
                            // If the texture's size is a multiple of the sampler size, enable interpolation using gl_FragCoord. (helps "invent" new integer values between scaled pixels)
                            result = -scale;
                            break;
                        }
                    }

                    result = scale;
                    break;

                case ShaderStage.Compute:
                    if ((binding.Flags & TextureUsageFlags.ResScaleUnsupported) != 0)
                    {
                        changed |= texture.ScaleMode != TextureScaleMode.Blacklisted;
                        texture.BlacklistScale();
                    }

                    result = texture.ScaleFactor;
                    break;
                }
            }

            if (result != _scales[index])
            {
                _scaleChanged = true;

                _scales[index] = result;
            }

            return(changed);
        }
        /// <summary>
        /// Ensures that the image bindings are visible to the host GPU.
        /// Note: this actually performs the binding using the host graphics API.
        /// </summary>
        /// <param name="pool">The current texture pool</param>
        /// <param name="stage">The shader stage using the textures to be bound</param>
        /// <param name="stageIndex">The stage number of the specified shader stage</param>
        private void CommitImageBindings(TexturePool pool, ShaderStage stage, int stageIndex)
        {
            if (_imageBindings[stageIndex] == null)
            {
                return;
            }

            for (int index = 0; index < _imageBindings[stageIndex].Length; index++)
            {
                TextureBindingInfo binding = _imageBindings[stageIndex][index];

                int packedId  = ReadPackedId(stageIndex, binding.Handle, _textureBufferIndex);
                int textureId = UnpackTextureId(packedId);

                Texture texture = pool.Get(textureId);

                if ((binding.Flags & TextureUsageFlags.ResScaleUnsupported) != 0)
                {
                    texture?.BlacklistScale();
                }

                ITexture hostTexture = texture?.GetTargetTexture(binding.Target);

                if (_imageState[stageIndex][index].Texture != hostTexture || _rebind)
                {
                    _imageState[stageIndex][index].Texture = hostTexture;

                    _context.Renderer.Pipeline.SetImage(index, stage, hostTexture);
                }
            }
        }
Exemple #3
0
        /// <summary>
        /// Propagates the scale between this texture and another to ensure they have the same scale.
        /// If one texture is blacklisted from scaling, the other will become blacklisted too.
        /// </summary>
        /// <param name="other">The other texture</param>
        public void PropagateScale(Texture other)
        {
            if (other.ScaleMode == TextureScaleMode.Blacklisted || ScaleMode == TextureScaleMode.Blacklisted)
            {
                BlacklistScale();
                other.BlacklistScale();
            }
            else
            {
                // Prefer the configured scale if present. If not, prefer the max.
                float targetScale = GraphicsConfig.ResScale;
                float sharedScale = (ScaleFactor == targetScale || other.ScaleFactor == targetScale) ? targetScale : Math.Max(ScaleFactor, other.ScaleFactor);

                SetScale(sharedScale);
                other.SetScale(sharedScale);
            }
        }
Exemple #4
0
        /// <summary>
        /// Ensures that the image bindings are visible to the host GPU.
        /// Note: this actually performs the binding using the host graphics API.
        /// </summary>
        /// <param name="pool">The current texture pool</param>
        /// <param name="stage">The shader stage using the textures to be bound</param>
        /// <param name="stageIndex">The stage number of the specified shader stage</param>
        private void CommitImageBindings(TexturePool pool, ShaderStage stage, int stageIndex)
        {
            if (_imageBindings[stageIndex] == null)
            {
                return;
            }

            for (int index = 0; index < _imageBindings[stageIndex].Length; index++)
            {
                TextureBindingInfo binding = _imageBindings[stageIndex][index];

                int packedId  = ReadPackedId(stageIndex, binding.Handle, _textureBufferIndex);
                int textureId = UnpackTextureId(packedId);

                Texture texture = pool.Get(textureId);

                if ((binding.Flags & TextureUsageFlags.ResScaleUnsupported) != 0)
                {
                    texture?.BlacklistScale();
                }

                ITexture hostTexture = texture?.GetTargetTexture(binding.Target);

                if (hostTexture != null && texture.Info.Target == Target.TextureBuffer)
                {
                    // Ensure that the buffer texture is using the correct buffer as storage.
                    // Buffers are frequently re-created to accomodate larger data, so we need to re-bind
                    // to ensure we're not using a old buffer that was already deleted.
                    _context.Methods.BufferManager.SetBufferTextureStorage(hostTexture, texture.Address, texture.Size, _isCompute);
                }

                if (_imageState[stageIndex][index].Texture != hostTexture || _rebind)
                {
                    _imageState[stageIndex][index].Texture = hostTexture;

                    Format format = binding.Format;

                    if (format == 0)
                    {
                        format = texture.Format;
                    }

                    _context.Renderer.Pipeline.SetImage(index, stage, hostTexture, format);
                }
            }
        }
Exemple #5
0
        /// <summary>
        /// Updates the Render Target scale, given the currently bound render targets.
        /// This will update scale to match the configured scale, scale textures that are eligible but not scaled,
        /// and propagate blacklisted status from one texture to the ones bound with it.
        /// </summary>
        /// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param>
        public void UpdateRenderTargetScale(int singleUse)
        {
            // Make sure all scales for render targets are at the highest they should be. Blacklisted targets should propagate their scale to the other targets.
            bool  mismatch     = false;
            bool  blacklisted  = false;
            bool  hasUpscaled  = false;
            bool  hasUndesired = false;
            float targetScale  = GraphicsConfig.ResScale;

            void ConsiderTarget(Texture target)
            {
                if (target == null)
                {
                    return;
                }
                float scale = target.ScaleFactor;

                switch (target.ScaleMode)
                {
                case TextureScaleMode.Blacklisted:
                    mismatch   |= scale != 1f;
                    blacklisted = true;
                    break;

                case TextureScaleMode.Eligible:
                    mismatch = true;     // We must make a decision.
                    break;

                case TextureScaleMode.Undesired:
                    hasUndesired = true;
                    mismatch    |= scale != 1f || hasUpscaled;  // If another target is upscaled, scale this one up too.
                    break;

                case TextureScaleMode.Scaled:
                    hasUpscaled = true;
                    mismatch   |= hasUndesired || scale != targetScale;   // If the target scale has changed, reset the scale for all targets.
                    break;
                }
            }

            if (singleUse != -1)
            {
                // If only one target is in use (by a clear, for example) the others do not need to be checked for mismatching scale.
                ConsiderTarget(_rtColors[singleUse]);
            }
            else
            {
                foreach (Texture color in _rtColors)
                {
                    ConsiderTarget(color);
                }
            }

            ConsiderTarget(_rtDepthStencil);

            mismatch |= blacklisted && hasUpscaled;

            if (blacklisted || (hasUndesired && !hasUpscaled))
            {
                targetScale = 1f;
            }

            if (mismatch)
            {
                if (blacklisted)
                {
                    // Propagate the blacklisted state to the other textures.
                    foreach (Texture color in _rtColors)
                    {
                        color?.BlacklistScale();
                    }

                    _rtDepthStencil?.BlacklistScale();
                }
                else
                {
                    // Set the scale of the other textures.
                    foreach (Texture color in _rtColors)
                    {
                        color?.SetScale(targetScale);
                    }

                    _rtDepthStencil?.SetScale(targetScale);
                }
            }

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

            bool isScalable = IsUpscaleCompatible(info);

            TextureScaleMode scaleMode = TextureScaleMode.Blacklisted;

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

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

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

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

                    overlap.SynchronizeMemory();

                    return(overlap);
                }
            }

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

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

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

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

            Texture texture = null;

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

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

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

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

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

                    break;
                }
            }

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

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

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

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

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

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

                            texture.PropagateScale(overlap);
                        }

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

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

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

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

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

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

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

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

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

            _textures.Add(texture);

            ShrinkOverlapsBufferIfNeeded();

            return(texture);
        }
Exemple #8
0
        /// <summary>
        /// Ensures that the texture bindings are visible to the host GPU.
        /// Note: this actually performs the binding using the host graphics API.
        /// </summary>
        /// <param name="pool">The current texture pool</param>
        /// <param name="stage">The shader stage using the textures to be bound</param>
        /// <param name="stageIndex">The stage number of the specified shader stage</param>
        private void CommitTextureBindings(TexturePool pool, ShaderStage stage, int stageIndex)
        {
            if (_textureBindings[stageIndex] == null)
            {
                return;
            }

            bool changed = false;

            for (int index = 0; index < _textureBindings[stageIndex].Length; index++)
            {
                TextureBindingInfo binding = _textureBindings[stageIndex][index];

                int packedId;

                if (binding.IsBindless)
                {
                    ulong address;

                    var bufferManager = _context.Methods.BufferManager;

                    if (_isCompute)
                    {
                        address = bufferManager.GetComputeUniformBufferAddress(binding.CbufSlot);
                    }
                    else
                    {
                        address = bufferManager.GetGraphicsUniformBufferAddress(stageIndex, binding.CbufSlot);
                    }

                    packedId = _context.PhysicalMemory.Read <int>(address + (ulong)binding.CbufOffset * 4);
                }
                else
                {
                    packedId = ReadPackedId(stageIndex, binding.Handle, _textureBufferIndex);
                }

                int textureId = UnpackTextureId(packedId);
                int samplerId;

                if (_samplerIndex == SamplerIndex.ViaHeaderIndex)
                {
                    samplerId = textureId;
                }
                else
                {
                    samplerId = UnpackSamplerId(packedId);
                }

                Texture texture = pool.Get(textureId);

                if ((binding.Flags & TextureUsageFlags.ResScaleUnsupported) != 0)
                {
                    texture?.BlacklistScale();
                }

                ITexture hostTexture = texture?.GetTargetTexture(binding.Target);

                if (_textureState[stageIndex][index].Texture != hostTexture || _rebind)
                {
                    _textureState[stageIndex][index].Texture = hostTexture;

                    _context.Renderer.Pipeline.SetTexture(index, stage, hostTexture);

                    changed = true;
                }

                if (hostTexture != null && texture.Info.Target == Target.TextureBuffer)
                {
                    // Ensure that the buffer texture is using the correct buffer as storage.
                    // Buffers are frequently re-created to accomodate larger data, so we need to re-bind
                    // to ensure we're not using a old buffer that was already deleted.
                    _context.Methods.BufferManager.SetBufferTextureStorage(hostTexture, texture.Address, texture.Size, _isCompute);
                }

                Sampler sampler = _samplerPool.Get(samplerId);

                ISampler hostSampler = sampler?.HostSampler;

                if (_textureState[stageIndex][index].Sampler != hostSampler || _rebind)
                {
                    _textureState[stageIndex][index].Sampler = hostSampler;

                    _context.Renderer.Pipeline.SetSampler(index, stage, hostSampler);
                }
            }

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

            bool isScalable = IsUpscaleCompatible(info);

            TextureScaleMode scaleMode = TextureScaleMode.Blacklisted;

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

            ulong address;

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

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

            int sameAddressOverlapsCount;

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

            Texture texture = null;

            TextureMatchQuality bestQuality = TextureMatchQuality.NoMatch;

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

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

                if (!rangeMatches)
                {
                    continue;
                }

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

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

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

                ChangeSizeIfNeeded(info, texture, isSamplerTexture, sizeHint);

                texture.SynchronizeMemory();

                return(texture);
            }

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

            ulong size = (ulong)sizeInfo.TotalSize;

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

            // Find view compatible matches.
            int overlapsCount;

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

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

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

                    if (!isSamplerTexture)
                    {
                        info = oInfo;
                    }

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

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

                    ChangeSizeIfNeeded(info, texture, isSamplerTexture, sizeHint);

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

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

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

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

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

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

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

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

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

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

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

                        setData |= modified || flush;

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

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

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

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

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

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

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

                        texture.PropagateScale(overlap);
                    }

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

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

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

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

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

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

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

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

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

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

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

            ShrinkOverlapsBufferIfNeeded();

            return(texture);
        }