/// <summary> /// Sets a transform feedback buffer on the graphics pipeline. /// The output from the vertex transformation stages are written into the feedback buffer. /// </summary> /// <param name="index">Index of the transform feedback buffer</param> /// <param name="gpuVa">Start GPU virtual address of the buffer</param> /// <param name="size">Size in bytes of the transform feedback buffer</param> public void SetTransformFeedbackBuffer(int index, ulong gpuVa, ulong size) { ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size); _transformFeedbackBuffers[index] = new BufferBounds(address, size); _transformFeedbackBuffersDirty = true; }
/// <summary> /// Sets a transform feedback buffer on the graphics pipeline. /// The output from the vertex transformation stages are written into the feedback buffer. /// </summary> /// <param name="index">Index of the transform feedback buffer</param> /// <param name="gpuVa">Start GPU virtual address of the buffer</param> /// <param name="size">Size in bytes of the transform feedback buffer</param> public void SetTransformFeedbackBuffer(int index, ulong gpuVa, ulong size) { ulong address = TranslateAndCreateBuffer(gpuVa, size); _transformFeedbackBuffers[index] = new BufferBounds(address, size); _transformFeedbackBuffersDirty = true; }
/// <summary> /// Binds a buffer on the host API. /// </summary> /// <param name="index">Index to bind the buffer into</param> /// <param name="stage">Shader stage to bind the buffer into</param> /// <param name="bounds">Buffer address and size</param> /// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffer</param> private void BindBuffer(int index, ShaderStage stage, BufferBounds bounds, bool isStorage) { BufferRange buffer = GetBufferRange(bounds.Address, bounds.Size); if (isStorage) { _context.Renderer.Pipeline.SetStorageBuffer(index, stage, buffer); } else { _context.Renderer.Pipeline.SetUniformBuffer(index, stage, buffer); } }
/// <summary> /// Ensures that the compute engine bindings are visible to the host GPU. /// Note: this actually performs the binding using the host graphics API. /// </summary> public void CommitComputeBindings() { uint enableMask = _cpStorageBuffers.EnableMask; for (int index = 0; (enableMask >> index) != 0; index++) { if ((enableMask & (1u << index)) == 0) { continue; } BufferBounds bounds = _cpStorageBuffers.Buffers[index]; if (bounds.Address == 0) { continue; } BufferRange buffer = GetBufferRange(bounds.Address, bounds.Size); int bindingPoint = CurrentShaderMeta(ShaderStage.Compute).GetStorageBufferBindingPoint(ShaderStage.Compute, index); _context.Renderer.Pipeline.SetStorageBuffer(bindingPoint, buffer); } enableMask = _cpUniformBuffers.EnableMask; for (int index = 0; (enableMask >> index) != 0; index++) { if ((enableMask & (1u << index)) == 0) { continue; } BufferBounds bounds = _cpUniformBuffers.Buffers[index]; if (bounds.Address == 0) { continue; } BufferRange buffer = GetBufferRange(bounds.Address, bounds.Size); int bindingPoint = CurrentShaderMeta(ShaderStage.Compute).GetUniformBufferBindingPoint(ShaderStage.Compute, index); _context.Renderer.Pipeline.SetUniformBuffer(bindingPoint, buffer); } // Force rebind after doing compute work. _rebind = true; }
/// <summary> /// Ensures that the compute engine bindings are visible to the host GPU. /// Note: this actually performs the binding using the host graphics API. /// </summary> public void CommitComputeBindings() { int sCount = _cpStorageBufferBindings; Span <BufferRange> sRanges = sCount < StackToHeapThreshold ? stackalloc BufferRange[sCount] : new BufferRange[sCount]; for (int index = 0; index < _cpStorageBuffers.Count; index++) { ref var bindingInfo = ref _cpStorageBuffers.Bindings[index]; BufferBounds bounds = _cpStorageBuffers.Buffers[bindingInfo.Slot]; if (bounds.Address != 0) { sRanges[bindingInfo.Binding] = GetBufferRange(bounds.Address, bounds.Size, bounds.Flags.HasFlag(BufferUsageFlags.Write)); } }
/// <summary> /// Binds a buffer on the host API. /// </summary> /// <param name="index">Index to bind the buffer into</param> /// <param name="stage">Shader stage to bind the buffer into</param> /// <param name="bounds">Buffer address and size</param> /// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffer</param> private void BindBuffer(int index, ShaderStage stage, BufferBounds bounds, bool isStorage) { BufferRange buffer = GetBufferRange(bounds.Address, bounds.Size); if (isStorage) { int bindingPoint = CurrentShaderMeta(stage).GetStorageBufferBindingPoint(stage, index); _context.Renderer.Pipeline.SetStorageBuffer(bindingPoint, buffer); } else { int bindingPoint = CurrentShaderMeta(stage).GetUniformBufferBindingPoint(stage, index); _context.Renderer.Pipeline.SetUniformBuffer(bindingPoint, buffer); } }
/// <summary> /// Ensures that the compute engine bindings are visible to the host GPU. /// Note: this actually performs the binding using the host graphics API. /// </summary> public void CommitComputeBindings() { int sCount = _cpStorageBufferBindings; Span <BufferRange> sRanges = sCount < StackToHeapThreshold ? stackalloc BufferRange[sCount] : new BufferRange[sCount]; for (int index = 0; index < _cpStorageBuffers.Count; index++) { ref var bindingInfo = ref _cpStorageBuffers.Bindings[index]; BufferBounds bounds = _cpStorageBuffers.Buffers[bindingInfo.Slot]; if (bounds.Address != 0) { // The storage buffer size is not reliable (it might be lower than the actual size), // so we bind the entire buffer to allow otherwise out of range accesses to work. sRanges[bindingInfo.Binding] = GetBufferRangeTillEnd( bounds.Address, bounds.Size, bounds.Flags.HasFlag(BufferUsageFlags.Write)); } }
private void BindBuffers(BufferCache bufferCache, BuffersPerStage[] bindings, bool isStorage) { int rangesFirst = 0; int rangesCount = 0; Span <BufferRange> ranges = _ranges; for (ShaderStage stage = ShaderStage.Vertex; stage <= ShaderStage.Fragment; stage++) { ref var buffers = ref bindings[(int)stage - 1]; for (int index = 0; index < buffers.Count; index++) { ref var bindingInfo = ref buffers.Bindings[index]; BufferBounds bounds = buffers.Buffers[bindingInfo.Slot]; if (bounds.Address != 0) { var isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write); var range = isStorage ? bufferCache.GetBufferRangeTillEnd(bounds.Address, bounds.Size, isWrite) : bufferCache.GetBufferRange(bounds.Address, bounds.Size); if (rangesCount == 0) { rangesFirst = bindingInfo.Binding; } else if (bindingInfo.Binding != rangesFirst + rangesCount) { SetHostBuffers(ranges, rangesFirst, rangesCount, isStorage); rangesFirst = bindingInfo.Binding; rangesCount = 0; } ranges[rangesCount++] = range; } }
/// <summary> /// This binds buffers into the host API, or updates data for already bound buffers. /// </summary> /// <param name="bindings">Bindings to bind or update</param> /// <param name="bind">True to bind, false to update</param> /// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffer</param> private void BindOrUpdateBuffers(BuffersPerStage[] bindings, bool bind, bool isStorage = false) { for (ShaderStage stage = ShaderStage.Vertex; stage <= ShaderStage.Fragment; stage++) { uint enableMask = bindings[(int)stage - 1].EnableMask; if (enableMask == 0) { continue; } for (int index = 0; (enableMask >> index) != 0; index++) { if ((enableMask & (1u << index)) == 0) { continue; } BufferBounds bounds = bindings[(int)stage - 1].Buffers[index]; if (bounds.Address == 0) { continue; } if (bind) { BindBuffer(index, stage, bounds, isStorage); } else { SynchronizeBufferRange(bounds.Address, bounds.Size); } } } }
/// <summary> /// Sets the region of a buffer at a given slot. /// </summary> /// <param name="index">Buffer slot</param> /// <param name="address">Region virtual address</param> /// <param name="size">Region size in bytes</param> /// <param name="flags">Buffer usage flags</param> public void SetBounds(int index, ulong address, ulong size, BufferUsageFlags flags = BufferUsageFlags.None) { Buffers[index] = new BufferBounds(address, size, flags); }
/// <summary> /// Creates a new instance of the shader stage buffer information. /// </summary> /// <param name="count">Maximum amount of buffers that the shader stage can use</param> public BuffersPerStage(int count) { Bindings = new BufferDescriptor[count]; Buffers = new BufferBounds[count]; }
/// <summary> /// Ensures that the graphics engine bindings are visible to the host GPU. /// Note: this actually performs the binding using the host graphics API. /// </summary> public void CommitGraphicsBindings() { if (_indexBufferDirty || _rebind) { _indexBufferDirty = false; if (_indexBuffer.Address != 0) { BufferRange buffer = GetBufferRange(_indexBuffer.Address, _indexBuffer.Size); _context.Renderer.Pipeline.SetIndexBuffer(buffer, _indexBuffer.Type); } } else if (_indexBuffer.Address != 0) { SynchronizeBufferRange(_indexBuffer.Address, _indexBuffer.Size); } uint vbEnableMask = _vertexBuffersEnableMask; if (_vertexBuffersDirty || _rebind) { _vertexBuffersDirty = false; Span <VertexBufferDescriptor> vertexBuffers = stackalloc VertexBufferDescriptor[Constants.TotalVertexBuffers]; for (int index = 0; (vbEnableMask >> index) != 0; index++) { VertexBuffer vb = _vertexBuffers[index]; if (vb.Address == 0) { continue; } BufferRange buffer = GetBufferRange(vb.Address, vb.Size); vertexBuffers[index] = new VertexBufferDescriptor(buffer, vb.Stride, vb.Divisor); } _context.Renderer.Pipeline.SetVertexBuffers(vertexBuffers); } else { for (int index = 0; (vbEnableMask >> index) != 0; index++) { VertexBuffer vb = _vertexBuffers[index]; if (vb.Address == 0) { continue; } SynchronizeBufferRange(vb.Address, vb.Size); } } if (_transformFeedbackBuffersDirty || _rebind) { _transformFeedbackBuffersDirty = false; for (int index = 0; index < Constants.TotalTransformFeedbackBuffers; index++) { BufferBounds tfb = _transformFeedbackBuffers[index]; if (tfb.Address == 0) { _context.Renderer.Pipeline.SetTransformFeedbackBuffer(index, new BufferRange(BufferHandle.Null, 0, 0)); continue; } BufferRange buffer = GetBufferRange(tfb.Address, tfb.Size); _context.Renderer.Pipeline.SetTransformFeedbackBuffer(index, buffer); } } else { for (int index = 0; index < Constants.TotalTransformFeedbackBuffers; index++) { BufferBounds tfb = _transformFeedbackBuffers[index]; if (tfb.Address == 0) { continue; } SynchronizeBufferRange(tfb.Address, tfb.Size); } } if (_gpStorageBuffersDirty || _rebind) { _gpStorageBuffersDirty = false; BindBuffers(_gpStorageBuffers, isStorage: true); } else { UpdateBuffers(_gpStorageBuffers); } if (_gpUniformBuffersDirty || _rebind) { _gpUniformBuffersDirty = false; BindBuffers(_gpUniformBuffers, isStorage: false); } else { UpdateBuffers(_gpUniformBuffers); } _rebind = false; }
/// <summary> /// Ensures that the graphics engine bindings are visible to the host GPU. /// Note: this actually performs the binding using the host graphics API. /// </summary> public void CommitGraphicsBindings() { var bufferCache = _channel.MemoryManager.Physical.BufferCache; if (_indexBufferDirty || _rebind) { _indexBufferDirty = false; if (_indexBuffer.Address != 0) { BufferRange buffer = bufferCache.GetBufferRange(_indexBuffer.Address, _indexBuffer.Size); _context.Renderer.Pipeline.SetIndexBuffer(buffer, _indexBuffer.Type); } } else if (_indexBuffer.Address != 0) { bufferCache.SynchronizeBufferRange(_indexBuffer.Address, _indexBuffer.Size); } uint vbEnableMask = _vertexBuffersEnableMask; if (_vertexBuffersDirty || _rebind) { _vertexBuffersDirty = false; Span <VertexBufferDescriptor> vertexBuffers = stackalloc VertexBufferDescriptor[Constants.TotalVertexBuffers]; for (int index = 0; (vbEnableMask >> index) != 0; index++) { VertexBuffer vb = _vertexBuffers[index]; if (vb.Address == 0) { continue; } BufferRange buffer = bufferCache.GetBufferRange(vb.Address, vb.Size); vertexBuffers[index] = new VertexBufferDescriptor(buffer, vb.Stride, vb.Divisor); } _context.Renderer.Pipeline.SetVertexBuffers(vertexBuffers); } else { for (int index = 0; (vbEnableMask >> index) != 0; index++) { VertexBuffer vb = _vertexBuffers[index]; if (vb.Address == 0) { continue; } bufferCache.SynchronizeBufferRange(vb.Address, vb.Size); } } if (_transformFeedbackBuffersDirty || _rebind) { _transformFeedbackBuffersDirty = false; Span <BufferRange> tfbs = stackalloc BufferRange[Constants.TotalTransformFeedbackBuffers]; for (int index = 0; index < Constants.TotalTransformFeedbackBuffers; index++) { BufferBounds tfb = _transformFeedbackBuffers[index]; if (tfb.Address == 0) { tfbs[index] = BufferRange.Empty; continue; } tfbs[index] = bufferCache.GetBufferRange(tfb.Address, tfb.Size, write: true); } _context.Renderer.Pipeline.SetTransformFeedbackBuffers(tfbs); } else { for (int index = 0; index < Constants.TotalTransformFeedbackBuffers; index++) { BufferBounds tfb = _transformFeedbackBuffers[index]; if (tfb.Address == 0) { continue; } bufferCache.SynchronizeBufferRange(tfb.Address, tfb.Size); } } if (_gpStorageBuffersDirty || _rebind) { _gpStorageBuffersDirty = false; BindBuffers(bufferCache, _gpStorageBuffers, isStorage: true); } else { UpdateBuffers(_gpStorageBuffers); } if (_gpUniformBuffersDirty || _rebind) { _gpUniformBuffersDirty = false; BindBuffers(bufferCache, _gpUniformBuffers, isStorage: false); } else { UpdateBuffers(_gpUniformBuffers); } CommitBufferTextureBindings(); _rebind = false; }
/// <summary> /// Sets the region of a buffer at a given slot. /// </summary> /// <param name="index">Buffer slot</param> /// <param name="address">Region virtual address</param> /// <param name="size">Region size in bytes</param> public void SetBounds(int index, ulong address, ulong size) { Buffers[index] = new BufferBounds(address, size); }