Пример #1
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);

                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);
                }
            }
        }
Пример #2
0
        /// <summary>
        /// Constructs a new instance of the texture bindings manager.
        /// </summary>
        /// <param name="context">The GPU context that the texture bindings manager belongs to</param>
        /// <param name="channel">The GPU channel that the texture bindings manager belongs to</param>
        /// <param name="poolCache">Texture pools cache used to get texture pools from</param>
        /// <param name="scales">Array where the scales for the currently bound textures are stored</param>
        /// <param name="isCompute">True if the bindings manager is used for the compute engine</param>
        public TextureBindingsManager(GpuContext context, GpuChannel channel, TexturePoolCache poolCache, float[] scales, bool isCompute)
        {
            _context          = context;
            _channel          = channel;
            _texturePoolCache = poolCache;
            _scales           = scales;
            _isCompute        = isCompute;

            int stages = isCompute ? 1 : Constants.ShaderStages;

            _textureBindings = new TextureBindingInfo[stages][];
            _imageBindings   = new TextureBindingInfo[stages][];

            _textureState = new TextureStatePerStage[stages][];
            _imageState   = new TextureStatePerStage[stages][];

            _textureBindingsCount = new int[stages];
            _imageBindingsCount   = new int[stages];

            for (int stage = 0; stage < stages; stage++)
            {
                _textureBindings[stage] = new TextureBindingInfo[InitialTextureStateSize];
                _imageBindings[stage]   = new TextureBindingInfo[InitialImageStateSize];

                _textureState[stage] = new TextureStatePerStage[InitialTextureStateSize];
                _imageState[stage]   = new TextureStatePerStage[InitialImageStateSize];
            }
        }
Пример #3
0
        /// <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)
            {
                if ((binding.Flags & TextureUsageFlags.ResScaleUnsupported) != 0)
                {
                    changed = texture.ScaleMode != TextureScaleMode.Blacklisted;
                    texture.BlacklistScale();
                }
                else
                {
                    switch (stage)
                    {
                    case ShaderStage.Fragment:
                        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.Vertex:
                        int fragmentIndex = (int)ShaderStage.Fragment - 1;
                        index += _textureBindingsCount[fragmentIndex] + _imageBindingsCount[fragmentIndex];

                        result = texture.ScaleFactor;
                        break;

                    case ShaderStage.Compute:
                        result = texture.ScaleFactor;
                        break;
                    }
                }
            }

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

                _scales[index] = result;
            }

            return(changed);
        }
Пример #4
0
        /// <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)
            {
                _scaleChanged |= true;

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

                    float scale = texture.ScaleFactor;

                    TextureManager manager = _context.Methods.TextureManager;

                    if (scale != 1)
                    {
                        Texture activeTarget = manager.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;
                }
            }

            _scales[index] = result;
            return(changed);
        }
Пример #5
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;
            }

            // Scales for images appear after the texture ones.
            int baseScaleIndex = _textureBindings[stageIndex]?.Length ?? 0;

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

                int textureBufferIndex = bindingInfo.CbufSlot < 0 ? _textureBufferIndex : bindingInfo.CbufSlot;

                int packedId  = ReadPackedId(stageIndex, bindingInfo.Handle, textureBufferIndex);
                int textureId = UnpackTextureId(packedId);

                Texture texture = pool.Get(textureId);

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

                if (hostTexture != null && texture.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)
                {
                    if (UpdateScale(texture, bindingInfo, baseScaleIndex + index, stage))
                    {
                        hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
                    }

                    _imageState[stageIndex][index].Texture = hostTexture;

                    Format format = bindingInfo.Format;

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

                    _context.Renderer.Pipeline.SetImage(bindingInfo.Binding, hostTexture, format);
                }
            }
        }
Пример #6
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);
                }
            }
        }
Пример #7
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;
            }

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

                int textureBufferIndex = bindingInfo.CbufSlot < 0 ? _textureBufferIndex : bindingInfo.CbufSlot;

                int packedId  = ReadPackedId(stageIndex, bindingInfo.Handle, textureBufferIndex);
                int textureId = UnpackTextureId(packedId);
                int samplerId;

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

                Texture texture = pool.Get(textureId);

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

                if (_textureState[stageIndex][index].Texture != hostTexture || _rebind)
                {
                    if (UpdateScale(texture, bindingInfo, index, stage))
                    {
                        hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
                    }

                    _textureState[stageIndex][index].Texture = hostTexture;

                    _context.Renderer.Pipeline.SetTexture(bindingInfo.Binding, hostTexture);
                }

                if (hostTexture != null && texture.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(bindingInfo.Binding, hostSampler);
                }
            }
        }
Пример #8
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)
        {
            int imageCount = _imageBindingsCount[stageIndex];

            if (imageCount == 0)
            {
                return;
            }

            if (pool == null)
            {
                Logger.Error?.Print(LogClass.Gpu, $"Shader stage \"{stage}\" uses images, but texture pool was not set.");
                return;
            }

            // Scales for images appear after the texture ones.
            int baseScaleIndex = _textureBindingsCount[stageIndex];

            for (int index = 0; index < imageCount; index++)
            {
                TextureBindingInfo bindingInfo = _imageBindings[stageIndex][index];

                (int textureBufferIndex, int samplerBufferIndex) = TextureHandle.UnpackSlots(bindingInfo.CbufSlot, _textureBufferIndex);

                int packedId  = ReadPackedId(stageIndex, bindingInfo.Handle, textureBufferIndex, samplerBufferIndex);
                int textureId = UnpackTextureId(packedId);

                Texture texture = pool.Get(textureId);

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

                bool isStore = bindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore);

                if (hostTexture != null && texture.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.

                    Format format = bindingInfo.Format;

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

                    _channel.BufferManager.SetBufferTextureStorage(hostTexture, texture.Range.GetSubRange(0).Address, texture.Size, bindingInfo, format, true);
                }
                else
                {
                    if (isStore)
                    {
                        texture?.SignalModified();
                    }

                    if (_imageState[stageIndex][index].Texture != hostTexture || _rebind)
                    {
                        if (UpdateScale(texture, bindingInfo, baseScaleIndex + index, stage))
                        {
                            hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
                        }

                        _imageState[stageIndex][index].Texture = hostTexture;

                        Format format = bindingInfo.Format;

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

                        _context.Renderer.Pipeline.SetImage(bindingInfo.Binding, hostTexture, format);
                    }
                }
            }
        }
Пример #9
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)
        {
            int textureCount = _textureBindingsCount[stageIndex];

            if (textureCount == 0)
            {
                return;
            }

            var samplerPool = _samplerPool;

            if (pool == null)
            {
                Logger.Error?.Print(LogClass.Gpu, $"Shader stage \"{stage}\" uses textures, but texture pool was not set.");
                return;
            }

            for (int index = 0; index < textureCount; index++)
            {
                TextureBindingInfo bindingInfo = _textureBindings[stageIndex][index];

                (int textureBufferIndex, int samplerBufferIndex) = TextureHandle.UnpackSlots(bindingInfo.CbufSlot, _textureBufferIndex);

                int packedId  = ReadPackedId(stageIndex, bindingInfo.Handle, textureBufferIndex, samplerBufferIndex);
                int textureId = UnpackTextureId(packedId);
                int samplerId;

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

                Texture texture = pool.Get(textureId);

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

                if (hostTexture != null && texture.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.
                    _channel.BufferManager.SetBufferTextureStorage(hostTexture, texture.Range.GetSubRange(0).Address, texture.Size, bindingInfo, bindingInfo.Format, false);
                }
                else
                {
                    if (_textureState[stageIndex][index].Texture != hostTexture || _rebind)
                    {
                        if (UpdateScale(texture, bindingInfo, index, stage))
                        {
                            hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
                        }

                        _textureState[stageIndex][index].Texture = hostTexture;

                        _context.Renderer.Pipeline.SetTexture(bindingInfo.Binding, hostTexture);
                    }

                    Sampler sampler = samplerPool?.Get(samplerId);

                    ISampler hostSampler = sampler?.GetHostSampler(texture);

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

                        _context.Renderer.Pipeline.SetSampler(bindingInfo.Binding, hostSampler);
                    }
                }
            }
        }
Пример #10
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;
            }

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

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

                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);
                }
            }
        }
Пример #11
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;
            }

            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 = MemoryMarshal.Cast <byte, int>(_context.PhysicalMemory.GetSpan(address + (ulong)binding.CbufOffset * 4, 4))[0];
                }
                else
                {
                    packedId = ReadPackedId(stageIndex, binding.Handle);
                }

                int textureId = UnpackTextureId(packedId);
                int samplerId;

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

                Texture texture = pool.Get(textureId);

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

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

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

                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(CurrentShaderMeta().GetTextureUnit(stage, index), hostSampler);
                }
            }
        }
Пример #12
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;
            }

            // Scales for images appear after the texture ones.
            int baseScaleIndex = _textureBindings[stageIndex]?.Length ?? 0;

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

                int textureBufferIndex;
                int samplerBufferIndex;

                if (bindingInfo.CbufSlot < 0)
                {
                    textureBufferIndex = _textureBufferIndex;
                    samplerBufferIndex = textureBufferIndex;
                }
                else
                {
                    textureBufferIndex = bindingInfo.CbufSlot & SlotMask;
                    samplerBufferIndex = ((bindingInfo.CbufSlot >> SlotHigh) != 0) ? (bindingInfo.CbufSlot >> SlotHigh) - 1 : textureBufferIndex;
                }

                int packedId  = ReadPackedId(stageIndex, bindingInfo.Handle, textureBufferIndex, samplerBufferIndex);
                int textureId = UnpackTextureId(packedId);

                Texture texture = pool.Get(textureId);

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

                bool isStore = bindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore);

                if (hostTexture != null && texture.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.

                    Format format = bindingInfo.Format;

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

                    _context.Methods.BufferManager.SetBufferTextureStorage(hostTexture, texture.Range.GetSubRange(0).Address, texture.Size, bindingInfo, format, true);
                }
                else if (isStore)
                {
                    texture?.SignalModified();
                }

                if (_imageState[stageIndex][index].Texture != hostTexture || _rebind)
                {
                    if (UpdateScale(texture, bindingInfo, baseScaleIndex + index, stage))
                    {
                        hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
                    }

                    _imageState[stageIndex][index].Texture = hostTexture;

                    Format format = bindingInfo.Format;

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

                    _context.Renderer.Pipeline.SetImage(bindingInfo.Binding, hostTexture, format);
                }
            }
        }