using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Net.Http.Headers; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Text; namespace Veldrid.Common.Plot { public class LineSeries: BaseVeldridRender,IZoomRender { public Vector2 WindowScale { get; set; } = Vector2.One; public float HScale { get; set; } = 1; public float VScale { get; set; } = 1; public float HOffset { get; set; } public float VOffset { get; set; } public bool ZoomEnbled { get; set; } private const uint ThreadGroupSizeX = 1000; private ProjView projView; Pipeline pipeline; private PlotInfo plotInfo; DeviceBuffer plotInfoBuffer; DeviceBuffer projViewBuffer; DeviceBuffer dataBuffer; ResourceLayout dataLayout; ResourceSet dataSet; ResourceLayout infoLayout; ResourceSet infoSet; Shader[] shaders; DeviceBuffer resultBuffer; DeviceBuffer cpuBuffer; ResourceLayout computeLayout; ResourceSet computeSet; Pipeline computePipline; ResourceLayout computeLineInfoLayout; ResourceSet computeLineInfoSet; Shader computeShader; CommandList computeCommandList; private object locker = new object(); public LineSeries(VeldridContent content, uint datalen=1000,uint plotcount=4):base(content) { dataLenght = datalen; PlotInfos = new SeriesInfo[plotcount]; Margin = new Padding(40, 10, 10, 20); } internal override void DisposeResources() { base.DisposeResources(); pipeline?.Dispose(); plotInfoBuffer?.Dispose(); projViewBuffer?.Dispose(); dataBuffer?.Dispose(); dataLayout?.Dispose(); dataSet?.Dispose(); infoLayout?.Dispose(); infoSet?.Dispose(); resultBuffer?.Dispose(); cpuBuffer?.Dispose(); computeLayout?.Dispose(); computeSet?.Dispose(); computePipline?.Dispose(); computeCommandList?.Dispose(); computeLineInfoLayout?.Dispose(); computeLineInfoSet?.Dispose(); computeShader?.Dispose(); } private void CreateDataBuffer() { if (DataLenght == 0 || PlotInfos ==null|| PlotInfos.Length == 0) return; lock (locker) { dataBuffer?.Dispose(); pipeline?.Dispose(); dataBuffer = ResourceFactory.CreateBuffer(new BufferDescription() { SizeInBytes = (uint)(DataLenght * PlotInfos.Length * 4), Usage = BufferUsage.StructuredBufferReadWrite, StructureByteStride = 4, }); dataSet = ResourceFactory.CreateResourceSet(new ResourceSetDescription(dataLayout, dataBuffer)); pipeline = CreatePipLine(PrimitiveTopology.LineStrip, new ResourceLayout[] { dataLayout, infoLayout }, new VertexLayoutDescription[0],new[] { shaders[0], shaders[1] }); datastartindex = 0; datatotallen = 0; TotalCount = 0; MaxAndMin = new Vector2[PlotInfos.Length]; CreateComputeBuffer(); } } internal override void CreateResources() { base.CreateResources(); plotInfoBuffer = ResourceFactory.CreateBuffer(new BufferDescription() { SizeInBytes = (uint)Unsafe.SizeOf(), Usage = BufferUsage.UniformBuffer, }); projViewBuffer = ResourceFactory.CreateBuffer(new BufferDescription() { SizeInBytes = (uint)Unsafe.SizeOf(), Usage = BufferUsage.UniformBuffer , }); datatotallen = 0; datastartindex = 0; shaders = CreateShader("LineSeries"); dataLayout = ResourceFactory.CreateResourceLayout(new ResourceLayoutDescription(new ResourceLayoutElementDescription() { Name = "DataBuffer", Stages = ShaderStages.Vertex, Kind = ResourceKind.StructuredBufferReadOnly, })); infoLayout = ResourceFactory.CreateResourceLayout(new ResourceLayoutDescription( new ResourceLayoutElementDescription() { Name = "InfoBuffer", Kind = ResourceKind.UniformBuffer, Stages = ShaderStages.Vertex, }, new ResourceLayoutElementDescription() { Name = "ProjView", Kind = ResourceKind.UniformBuffer, Stages = ShaderStages.Vertex, })); infoSet = ResourceFactory.CreateResourceSet(new ResourceSetDescription(infoLayout, plotInfoBuffer, projViewBuffer)); resultBuffer = ResourceFactory.CreateBuffer(new BufferDescription(20 * 4, BufferUsage.StructuredBufferReadWrite, 4)); cpuBuffer = ResourceFactory.CreateBuffer(new BufferDescription(resultBuffer.SizeInBytes, BufferUsage.Staging)); computeLayout = ResourceFactory.CreateResourceLayout(new ResourceLayoutDescription( new ResourceLayoutElementDescription("DataBuffer", ResourceKind.StructuredBufferReadOnly, ShaderStages.Compute), new ResourceLayoutElementDescription("ResultDataBuffer", ResourceKind.StructuredBufferReadWrite, ShaderStages.Compute))); computeLineInfoLayout = ResourceFactory.CreateResourceLayout(new ResourceLayoutDescription( new ResourceLayoutElementDescription("InfoBuffer", ResourceKind.UniformBuffer, ShaderStages.Compute))); computeLineInfoSet = ResourceFactory.CreateResourceSet(new ResourceSetDescription(computeLineInfoLayout, plotInfoBuffer)); ShaderDescription shaderDescription = new ShaderDescription(ShaderStages.Compute, GLSLManger.LoadShader("LineSeries.hlsl"), "main") { Debug = true, }; computeShader = ResourceFactory.CreateShader(shaderDescription); computeCommandList = ResourceFactory.CreateCommandList() ; CreateDataBuffer(); } private void CreateComputeBuffer() { computeSet?.Dispose(); computePipline?.Dispose(); computeSet = ResourceFactory.CreateResourceSet(new ResourceSetDescription(computeLayout, dataBuffer, resultBuffer)); computePipline = ResourceFactory.CreateComputePipeline(new ComputePipelineDescription(computeShader, new[] { computeLayout, computeLineInfoLayout }, ThreadGroupSizeX, 1, 1)); } struct PlotInfo { public RgbaFloat Color; public uint PlotIndex; public float Interval; public uint DataStartIndex; public uint PlotDataLenght; } struct ProjView { public Matrix4x4 Proj; public Matrix4x4 View; } public void Clear() { datastartindex = 0; datatotallen = 0; TotalCount = 0; } public ulong TotalCount { get; private set; } public Vector2[] MaxAndMin { get; private set; } = new Vector2[0]; public unsafe void AddData(float[] data) { if (DataLenght == 0 || PlotInfos == null || PlotInfos.Length == 0) return; if (data == null|| data.Length!=PlotInfos.Length) return; TotalCount++; for(int i=0;i()),ref data[i],(uint)Unsafe.SizeOf()); } datastartindex++; datatotallen++; if(datatotallen>DataLenght)datatotallen = DataLenght; if (datastartindex >= DataLenght) datastartindex = 0; MaxAndMin = new Vector2[PlotInfos.Length]; //return; if (datatotallen > 0) { computeCommandList.Begin(); CalcMaxMin(computeCommandList); computeCommandList.End(); GraphicsDevice.SubmitCommands(computeCommandList); GraphicsDevice.WaitForIdle(); } } private unsafe void CalcMaxMin(CommandList command) { MaxAndMin = new Vector2[PlotInfos.Length]; plotInfo.DataStartIndex = datatotallen; plotInfo.PlotDataLenght = DataLenght; command.SetPipeline(computePipline); command.SetComputeResourceSet(0, computeSet); command.SetComputeResourceSet(1, computeLineInfoSet); for (uint i = 0; i < PlotInfos.Length; i++) { plotInfo.PlotIndex = i; command.UpdateBuffer(plotInfoBuffer, 0, plotInfo); command.Dispatch((uint)MathF.Ceiling(((float)datatotallen) / ThreadGroupSizeX), 1, 1); } command.CopyBuffer(resultBuffer, 0, cpuBuffer, 0, (uint)(MaxAndMin.Length * Unsafe.SizeOf())); var mapped = GraphicsDevice.Map(cpuBuffer, MapMode.ReadWrite); Unsafe.CopyBlock(ref Unsafe.As(ref MaxAndMin[0]), ref Unsafe.AsRef(mapped.Data.ToPointer()), (uint)(MaxAndMin.Length * Unsafe.SizeOf())); GraphicsDevice.Unmap(cpuBuffer); } internal unsafe override void DrawData() { if (DataLenght == 0 || PlotInfos == null || PlotInfos.Length == 0) return; lock (locker) { base.DrawData(); if (datatotallen == 0) return; //CalcMaxMin(CommandList); projView.Proj = OrthographicMatrix; projView.View = GetLineMatrix(HScale,VScale,0,VOffset); CommandList.SetFramebuffer(MainSwapchainBuffer); CommandList.UpdateBuffer(projViewBuffer, 0, projView); CommandList.UpdateBuffer(plotInfoBuffer, 0, plotInfo); CommandList.SetPipeline(pipeline); CommandList.SetGraphicsResourceSet(0, dataSet); CommandList.SetGraphicsResourceSet(1, infoSet); for (uint i = 0; i < PlotInfos.Length; i++) { if (!PlotInfos[i].Visibily) continue; plotInfo.Color = PlotInfos[i].Color; plotInfo.PlotDataLenght = DataLenght; plotInfo.DataStartIndex = datatotallen == DataLenght ? datastartindex : 0; plotInfo.PlotIndex = i; CommandList.UpdateBuffer(plotInfoBuffer, 0, plotInfo); CommandList.Draw(datatotallen); } } } private uint datastartindex = 0; private uint datatotallen = 0; private uint dataLenght = 1000; public float Interval { get => plotInfo.Interval; set => plotInfo.Interval = value; } public uint DataLenght { get => dataLenght; set { if (dataLenght != value) { dataLenght = value; CreateDataBuffer(); } } } public SeriesInfo[] PlotInfos = new SeriesInfo[0]; } public class SeriesInfo { public RgbaFloat Color; public bool Visibily; public string Name = string.Empty; } }