我在使用 WPF 和使用 DrawingContext
时遇到了一些严重的问题,或者特别是 VisualDrawingContext
来自覆盖元素上的 OnRender
或者如果使用 DrawingVisual.RenderOpen()
。
问题是分配了很多。例如,它似乎在每次使用绘图上下文时分配一个 byte[]
缓冲区。
如何使用绘图上下文的示例。
using (var drawingContext = m_drawingVisual.RenderOpen())
{
// Many different drawingContext.Draw calls
// E.g. DrawEllipse, DrawRectangle etc.
}
或
override void OnRender(DrawingContext drawingContext)
{
// Many different drawingContext.Draw calls
// E.g. DrawEllipse, DrawRectangle etc.
}
这会导致大量分配,导致一些不需要的垃圾回收。所以是的,我需要这个,请留在主题 :)。
在托管堆分配数量为零或较少的 WPF 中有哪些选择?重用对象很好,但我还没有找到一种方法来做到这一点......或者 DependencyProperty
及其周围/内部的分配没有问题。
我确实知道 WritableBitmapEx
,但希望有一个解决方案不涉及栅格化为预定义的位图,而是适当的“矢量”图形,例如仍然可以缩放。
注意:CPU 使用率是一个问题,但远不及由此造成的巨大垃圾压力。
更新:我正在寻找适用于 .NET Framework 4.5+ 的解决方案,如果更高版本中有任何内容,例如4.7 可能有助于回答这个问题,那很好。但它适用于桌面 .NET Framework。
更新 2:对两个主要场景的简要描述。所有示例都已使用 CLRProfiler
进行了分析,它清楚地表明由于此发生了大量分配,这对我们的用例来说是一个问题。请注意,这是旨在传达原则的示例代码,而不是确切的代码。
A:这种情况如下图所示。基本上,显示图像并通过自定义 DrawingVisualControl
绘制一些叠加图形,然后使用 using (var drawingContext = m_drawingVisual.RenderOpen())
获取绘图上下文然后通过它绘制。绘制了许多椭圆、矩形和文本。这个例子还展示了一些缩放的东西,这只是为了缩放等。
<Viewbox x:Name="ImageViewbox" VerticalAlignment="Center" HorizontalAlignment="Center">
<Grid x:Name="ImageGrid" SnapsToDevicePixels="True" ClipToBounds="True">
<Grid.LayoutTransform>
<ScaleTransform x:Name="ImageTransform" CenterX="0" CenterY="0"
ScaleX="{Binding ElementName=ImageScaleSlider, Path=Value}"
ScaleY="{Binding ElementName=ImageScaleSlider, Path=Value}" />
</Grid.LayoutTransform>
<Image x:Name="ImageSource" RenderOptions.BitmapScalingMode="NearestNeighbor" SnapsToDevicePixels="True"
MouseMove="ImageSource_MouseMove" />
<v:DrawingVisualControl x:Name="DrawingVisualControl" Visual="{Binding DrawingVisual}"
SnapsToDevicePixels="True"
RenderOptions.BitmapScalingMode="NearestNeighbor"
IsHitTestVisible="False" />
</Grid>
</Viewbox>
`DrawingVisualControl 定义为:
public class DrawingVisualControl : FrameworkElement
{
public DrawingVisual Visual
{
get { return GetValue(DrawingVisualProperty) as DrawingVisual; }
set { SetValue(DrawingVisualProperty, value); }
}
private void UpdateDrawingVisual(DrawingVisual visual)
{
var oldVisual = Visual;
if (oldVisual != null)
{
RemoveVisualChild(oldVisual);
RemoveLogicalChild(oldVisual);
}
AddVisualChild(visual);
AddLogicalChild(visual);
}
public static readonly DependencyProperty DrawingVisualProperty =
DependencyProperty.Register("Visual",
typeof(DrawingVisual),
typeof(DrawingVisualControl),
new FrameworkPropertyMetadata(OnDrawingVisualChanged));
private static void OnDrawingVisualChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var dcv = d as DrawingVisualControl;
if (dcv == null) { return; }
var visual = e.NewValue as DrawingVisual;
if (visual == null) { return; }
dcv.UpdateDrawingVisual(visual);
}
protected override int VisualChildrenCount
{
get { return (Visual != null) ? 1 : 0; }
}
protected override Visual GetVisualChild(int index)
{
return this.Visual;
}
}
B:第二种情况涉及绘制移动的数据“网格”,例如20 行 100 列,元素由边框和不同颜色的文本组成,以显示某些状态。网格根据外部输入移动,目前每秒仅更新 5-10 次。 30帧/秒会更好。因此,这将更新 ObservableCollection
中的 2000 个项目,该项目绑定(bind)到 ListBox
(使用 VirtualizingPanel.IsVirtualizing="True"
)和 ItemsPanel
是一个 Canvas
。我们甚至无法在我们的正常用例中展示这一点,因为它分配的太多以至于 GC 暂停变得太长和太频繁。
<ListBox x:Name="Items" Background="Black"
VirtualizingPanel.IsVirtualizing="True" SnapsToDevicePixels="True">
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type vm:ElementViewModel}">
<Border Width="{Binding Width_mm}" Height="{Binding Height_mm}"
Background="{Binding BackgroundColor}"
BorderBrush="{Binding BorderColor}"
BorderThickness="3">
<TextBlock Foreground="{Binding DrawColor}" Padding="0" Margin="0"
Text="{Binding TextResult}" FontSize="{Binding FontSize_mm}"
TextAlignment="Center" VerticalAlignment="Center"
HorizontalAlignment="Center"/>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Canvas.Left" Value="{Binding X_mm}"/>
<Setter Property="Canvas.Top" Value="{Binding Y_mm}"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<Canvas IsItemsHost="True"
Width="{Binding CanvasWidth_mm}"
Height="{Binding CanvasHeight_mm}"
/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
这里有很多数据绑定(bind),值类型的装箱确实会产生很多分配,但这不是这里的主要问题。它是由 WPF 完成的分配。
最佳答案
一些输入
您的代码段不可用,所以我只能提供建议。 在性能方面,使用 Microsoft 提供的分析工具。 您可以找到工具 here
您可以阅读的一个更重要的链接是 WPF graphics
注意:- 尝试使用绘图组
关于c# - WPF 中的低分配绘图,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44957522/