Note that there are some explanatory texts on larger screens.

plurals
  1. POFast 2D graphics in WPF
    primarykey
    data
    text
    <p>I need to draw a large amount of 2D elements in WPF, such as lines and polygons. Their position also needs to be updated constantly. </p> <p>I have looked at many of the answers here which mostly suggested using DrawingVisual or overriding the OnRender function. To test these methods I've implemented a simple particle system rendering 10000 ellipses and I find that the drawing performance is still really terrible using both of these approaches. On my PC I can't get much above 5-10 frames a second. which is totally unacceptable when you consider that I easily draw 1/2 million particles smoothly using other technologies.</p> <p>So my question is, am I running against a technical limitation here of WPF or am I missing something? Is there something else I can use? any suggestions welcome. </p> <p><strong>Here the code I tried</strong></p> <p>content of MainWindow.xaml:</p> <pre><code>&lt;Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="500" Width="500" Loaded="Window_Loaded"&gt; &lt;Grid Name="xamlGrid"&gt; &lt;/Grid&gt; &lt;/Window&gt; </code></pre> <p>content of MainWindow.xaml.cs:</p> <pre><code>using System.Windows.Threading; namespace WpfApplication1 { /// &lt;summary&gt; /// Interaction logic for MainWindow.xaml /// &lt;/summary&gt; public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } EllipseBounce[] _particles; DispatcherTimer _timer = new DispatcherTimer(); private void Window_Loaded(object sender, RoutedEventArgs e) { //particles with Ellipse Geometry _particles = new EllipseBounce[10000]; //define area particles can bounce around in Rect stage = new Rect(0, 0, 500, 500); //seed particles with random velocity and position Random rand = new Random(); //populate for (int i = 0; i &lt; _particles.Length; i++) { Point pos = new Point((float)(rand.NextDouble() * stage.Width + stage.X), (float)(rand.NextDouble() * stage.Height + stage.Y)); Point vel = new Point((float)(rand.NextDouble() * 5 - 2.5), (float)(rand.NextDouble() * 5 - 2.5)); _particles[i] = new EllipseBounce(stage, pos, vel, 2); } //add to particle system - this will draw particles via onrender method ParticleSystem ps = new ParticleSystem(_particles); //at this element to the grid (assumes we have a Grid in xaml named 'xmalGrid' xamlGrid.Children.Add(ps); //set up and update function for the particle position _timer.Tick += _timer_Tick; _timer.Interval = new TimeSpan(0, 0, 0, 0, 1000 / 60); //update at 60 fps _timer.Start(); } void _timer_Tick(object sender, EventArgs e) { for (int i = 0; i &lt; _particles.Length; i++) { _particles[i].Update(); } } } /// &lt;summary&gt; /// Framework elements that draws particles /// &lt;/summary&gt; public class ParticleSystem : FrameworkElement { private DrawingGroup _drawingGroup; public ParticleSystem(EllipseBounce[] particles) { _drawingGroup = new DrawingGroup(); for (int i = 0; i &lt; particles.Length; i++) { EllipseGeometry eg = particles[i].EllipseGeometry; Brush col = Brushes.Black; col.Freeze(); GeometryDrawing gd = new GeometryDrawing(col, null, eg); _drawingGroup.Children.Add(gd); } } protected override void OnRender(DrawingContext drawingContext) { base.OnRender(drawingContext); drawingContext.DrawDrawing(_drawingGroup); } } /// &lt;summary&gt; /// simple class that implements 2d particle movements that bounce from walls /// &lt;/summary&gt; public class SimpleBounce2D { protected Point _position; protected Point _velocity; protected Rect _stage; public SimpleBounce2D(Rect stage, Point pos,Point vel) { _stage = stage; _position = pos; _velocity = vel; } public double X { get { return _position.X; } } public double Y { get { return _position.Y; } } public virtual void Update() { UpdatePosition(); BoundaryCheck(); } private void UpdatePosition() { _position.X += _velocity.X; _position.Y += _velocity.Y; } private void BoundaryCheck() { if (_position.X &gt; _stage.Width + _stage.X) { _velocity.X = -_velocity.X; _position.X = _stage.Width + _stage.X; } if (_position.X &lt; _stage.X) { _velocity.X = -_velocity.X; _position.X = _stage.X; } if (_position.Y &gt; _stage.Height + _stage.Y) { _velocity.Y = -_velocity.Y; _position.Y = _stage.Height + _stage.Y; } if (_position.Y &lt; _stage.Y) { _velocity.Y = -_velocity.Y; _position.Y = _stage.Y; } } } /// &lt;summary&gt; /// extend simplebounce2d to add ellipse geometry and update position in the WPF construct /// &lt;/summary&gt; public class EllipseBounce : SimpleBounce2D { protected EllipseGeometry _ellipse; public EllipseBounce(Rect stage,Point pos, Point vel, float radius) : base(stage, pos, vel) { _ellipse = new EllipseGeometry(pos, radius, radius); } public EllipseGeometry EllipseGeometry { get { return _ellipse; } } public override void Update() { base.Update(); _ellipse.Center = _position; } } } </code></pre>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload