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