我一直在寻找一段时间来了解如何改善波形图的渲染性能。 目前我正在使用(根据我认为可能)优化的基于GDI的渲染例程:
Private Sub Calculate2(ByVal aData()() As Double)
'aData size: 1000 traces with 200k points each -> Dim aData(1000, 200000)
'Some data preparations doing roughly the same as they would in the real app
Dim PS_Y As Double = 1
Dim Origin As PointF = New PointF(Rnd() * 100, Rnd() * 100)
PS_Y = Rnd() + 0.1
Dim Data(), ST As Double
Dim lPoints As New List(Of PointF)
Dim PS_X As Double = Rnd() + 0.1
'Graphics initialisation
Dim Img As New Bitmap(900, 600)
Dim ImgGR As Graphics = Graphics.FromImage(Img)
ImgGR.Clear(Color.White)
Dim WFPen As New Pen(Brushes.Black, 1)
'Cache property values for faster access:
Dim l As Integer = 100 'ChartRect.Left
Dim r As Integer = 1000 'ChartRect.Right
'Process trace by trace:
For i = 0 To aData.Length - 1
ST = Rnd() 'x distance of the points
Data = aData(i) 'y values, 1 per x value
If Data.Length = 0 Then Continue For
'scale precalculations, first & last displayed points:
Dim ScaleX As Double = ST * PS_X
Dim OrigX As Single = Origin.X
Dim iStart As Integer = (l - OrigX) / ScaleX
Dim iEnd As Integer = (r - OrigX) / ScaleX
If iStart < 0 Then iStart = 0
If iEnd < 0 Then iEnd = 0
If iEnd > Data.Length - 1 Then iEnd = Data.Length - 1
If iStart > Data.Length - 1 Then iStart = Data.Length - 1
'Make sure that for benchmarking purposes all points are displayed, next 2 lines do not exist in real code:
iStart = 0
iEnd = Data.Length - 1
If iEnd < iStart Then Continue For
'point calculations using the pecalculated values:
Dim APT(iEnd - iStart) As PointF
For j = iStart To iEnd
APT(j - iStart) = Origin + New SizeF(j * ScaleX, -(Data(j) * PS_Y))
Next
ImgGR.DrawLines(WFPen, APT)
'Commenting out this line reduces the time needed for executing this whole routine from 42.4s to 4.76s
'Hence most of the time spent even with all the scaling is still in rendering the spline.
Next
我尝试过使用Direct2D的方法,但这比GDI中的“DrawLines”方法慢得多:
'Imports D2D = Microsoft.WindowsAPICodePack.DirectX.Direct2D1
'Imports DX = Microsoft.WindowsAPICodePack.DirectX
Dim TGT As D2D.RenderTarget
Private Sub initd2d()
Dim fac As D2D.D2DFactory = D2D.D2DFactory.CreateFactory(Microsoft.WindowsAPICodePack.DirectX.Direct2D1.D2DFactoryType.SingleThreaded)
Dim imgf As DX.WindowsImagingComponent.ImagingFactory
imgf = DX.WindowsImagingComponent.ImagingFactory.Create
'Dim pf As New D2D.PixelFormat(DX.Graphics.Format.B8G8R8A8UNorm, D2D.AlphaMode.Ignore)
Dim pf As New D2D.PixelFormat(DX.Graphics.Format.Unknown, D2D.AlphaMode.Unknown)
Dim bmp As DX.WindowsImagingComponent.ImagingBitmap
bmp = imgf.CreateImagingBitmap(CUInt(900), CUInt(600), DX.WindowsImagingComponent.PixelFormats.Pbgra32Bpp, DX.WindowsImagingComponent.BitmapCreateCacheOption.CacheOnLoad)
Dim rtp As New D2D.RenderTargetProperties(D2D.RenderTargetType.Default, pf, 0, 0, D2D.RenderTargetUsages.None, Microsoft.WindowsAPICodePack.DirectX.Direct3D.FeatureLevel.Default)
TGT = fac.CreateWicBitmapRenderTarget(bmp, rtp)
TGT.Clear(New D2D.ColorF(Color.White.ToArgb))
End Sub
'104,7s execution time:
Private Sub drawd2d()
Dim p1 As New D2D.Point2F(1, 10.5)
Dim p2 As New D2D.Point2F(1.01, 10)
Dim b As D2D.Brush = TGT.CreateSolidColorBrush(New D2D.ColorF(0, 0, 255))
TGT.BeginDraw()
For i = 0 To 200000 * 1000
TGT.DrawLine(p1, p2, b, 1)
Next
End Sub
数据维度通常用于此应用程序中,因此请不要问我为什么需要它。
我也知道必须以某种方式更快地显示它,因为首先生成数据的应用程序设法在大约3秒内渲染4个具有50M点的迹线,这大致与数据安装相同。
如果有人之前做过类似的事情,我非常感谢你,如果你能指出我正确的方向,或者如果可以的话,给我一个替代方案,我可以将PointF-Array或类似结构渲染成位图
编辑: 请记住,这些是BENCHMARKING例程,旨在与原始软件进行相同的计算,而无需加载程序的其余部分。 Data()()数组由软件动态生成,因此必须检查维度并对其采取行动。
清除功能已被删除,数据加载和图片显示功能,网格和其他与此问题无关的代码。
EDIT2: 代码示例包括数据生成例程:
Sub Main()
Dim T As New HiResTimer
Dim StartTime, StopTime As Long
'initd2d()
PrepareData(10, 200000)
StartTime = T.Value
For i = 1 To 1
'drawd2d()
Calculate2(100, 0, 200000)
Next
StopTime = T.Value
Dim Elapsed As Double = (StopTime - StartTime) / T.Frequency
Debug.Print("Time: " & Elapsed)
End Sub
Dim aData()() As Double
Private Sub PrepareData(ByVal WaveformCount As Integer, ByVal Length As Integer)
Dim Offset As Double = 0
Dim Amplitude As Double = 100
Dim SineCount As Double = 4
Dim SineBase As Double = 2 * Math.PI / Length * SineCount
ReDim aData(WaveformCount - 1)
For i = 0 To WaveformCount - 1
ReDim aData(i)(Length - 1)
For j = 0 To Length - 1
aData(i)(j) = Amplitude * Math.Sin(SineBase * j) + Offset + Rnd() * Amplitude * 0.05
Next
Next
End Sub
Private Sub Calculate2(ByVal AmplitudeUsed As Double, ByVal OffsetUsed As Double, ByVal LengthUsed As Integer)
Dim PS_Y As Double
'Instead of making this random, here a real calculation for the scale (chartheight / biggest waveform amplitude) :
PS_Y = 600 / (AmplitudeUsed * 2 + AmplitudeUsed * 0.1) ' Rnd() + 0.1
'Since our calculation method oscillates around zero with the same amplitude we can predict that we need the following offset:
Dim Origin As PointF = New PointF(0, 300)
Dim Data(), ST As Double
Dim lPoints As New List(Of PointF)
'set the x axis scale to make our waveform fit exactly:
Dim PS_X As Double = 900 / LengthUsed
Dim Img As New Bitmap(900, 600)
Dim ImgGR As Graphics = Graphics.FromImage(Img)
ImgGR.Clear(Color.White)
Dim WFPen As New Pen(Brushes.Black, 1)
'theese 2 values simply define an area in the picture where the waveforms are actually visible to not overlap with the axis / legend, set it to something that makes sense
Dim l As Integer = 20 'ChartRect.Left
Dim r As Integer = 700 'ChartRect.Right
For i = 0 To aData.Length - 1
'Set sampletime to 1 second to keep the predefined scale from above, but still do the calculation as it would be needed with real data:
ST = 1 ' Rnd()
Data = aData(i)
If Data.Length = 0 Then Continue For
Dim ScaleX As Double = ST * PS_X
Dim OrigX As Single = Origin.X
Dim iStart As Integer = (l - OrigX) / ScaleX
Dim iEnd As Integer = (r - OrigX) / ScaleX
If iStart < 0 Then iStart = 0
If iEnd < 0 Then iEnd = 0
If iEnd > Data.Length - 1 Then iEnd = Data.Length - 1
If iStart > Data.Length - 1 Then iStart = Data.Length - 1
iStart = 0
iEnd = Data.Length - 1
If iEnd < iStart Then Continue For
Dim APT(iEnd - iStart) As PointF
For j = iStart To iEnd
APT(j - iStart) = Origin + New SizeF(j * ScaleX, -(Data(j) * PS_Y))
Next
ImgGR.DrawLines(WFPen, APT)
Next
PictureBox1.Image = Img
End Sub
答案 0 :(得分:0)
您可以通过检查点之间的距离来加快速度,并跳过距离上一点太近的点。
您可以跳过距离计算中的sqrt,并在需要时进行其他优化。
要查看这是否有益,请尝试使用n / 2点运行基准测试。
答案 1 :(得分:0)
这里是自动优化图形的代码块,它对许多小波形没有帮助,但它对10k和更大的波形起作用:
Private Function Calculate3(ByVal aData()() As Double, ByVal AmplitudeUsed As Double, ByVal OffsetUsed As Double, ByVal LengthUsed As Integer) As Bitmap
Dim PS_Y As Double
'Instead of making this random, here a real calculation for the scale (chartheight / biggest waveform amplitude) :
PS_Y = 600 / (AmplitudeUsed * 2 + AmplitudeUsed * 0.1) ' Rnd() + 0.1
'Since our calculation method oscillates around zero with the same amplitude we can predict that we need the following offset:
Dim Origin As PointF = New PointF(0, 300)
Dim Data(), ST As Double
Dim lPoints As New List(Of PointF)
'set the x axis scale to make our waveform fit exactly:
Dim PS_X As Double = 900 / LengthUsed
Dim Img As New Bitmap(900, 600)
Dim ImgGR As Graphics = Graphics.FromImage(Img)
ImgGR.Clear(Color.White)
Dim WFPen As New Pen(Brushes.Black, 1)
'theese 2 values simply define an area in the picture where the waveforms are actually visible to not overlap with the axis / legend, set it to something that makes sense
Dim l As Integer = 20 'ChartRect.Left
Dim r As Integer = 700 'ChartRect.Right
Dim APT() As PointF
For i = 0 To aData.Length - 1
Dim iStart As Integer
Dim iEnd As Integer
Dim ScaleX As Double
Dim OrigX As Single
'Set sampletime to 1 second to keep the predefined scale from above, but still do the calculation as it would be needed with real data:
ST = 1 ' Rnd()
Data = aData(i)
If Data.Length = 0 Then Continue For
ScaleX = ST * PS_X
OrigX = Origin.X
iStart = (l - OrigX) / ScaleX
iEnd = (r - OrigX) / ScaleX
If iStart < 0 Then iStart = 0
If iEnd < 0 Then iEnd = 0
If iEnd > Data.Length - 1 Then iEnd = Data.Length - 1
If iStart > Data.Length - 1 Then iStart = Data.Length - 1
iStart = 0
iEnd = Data.Length - 1
If iEnd < iStart Then Continue For
If ScaleX < 0.3 Then 'more than 3 lines per point, summarize
Dim iPT As Integer
Dim FirstX As Integer
Dim LastX As Integer
Dim MinY As Single
Dim MaxY As Single
Dim tVal As Single
Dim iSt As Integer
Dim iEn As Integer
FirstX = Math.Truncate(iStart * ScaleX)
LastX = Math.Ceiling(iEnd * ScaleX)
ReDim APT((LastX - FirstX) * 2 - 1)
For iX = FirstX To LastX - 1
MinY = Single.MaxValue
MaxY = Single.MinValue
iSt = Math.Truncate(iX / ScaleX)
iEn = Math.Truncate((iX + 1) / ScaleX) - 1
If iSt < 0 Then iSt = 0
If iEn > Data.Length - 1 Then iEn = Data.Length - 1
For iDat = iSt To iEn
tVal = -Data(iDat) * PS_Y
If tVal > MaxY Then MaxY = tVal
If tVal < MinY Then MinY = tVal
Next
iPT = (iX - FirstX) * 2
APT(iPT) = Origin + New SizeF(iX, MinY)
APT(iPT + 1) = Origin + New SizeF(iX, MaxY)
Next
Else
ReDim APT(iEnd - iStart)
For j = iStart To iEnd
APT(j - iStart) = Origin + New SizeF(j * ScaleX, -(Data(j) * PS_Y))
Next
End If
ImgGR.DrawLines(WFPen, APT)
Next
Return Img
End Function
感谢大家抽出时间阅读我的问题