I need to display more than a million polygons on a usercontrol to display CAD inquiry. Because I need to be able to zoom in to view the polygons, they can't be drawn onto a bitmap. So in the OnRender override, I use StreamGeometry, StreamGeometryContext to generate polygon geometry. I was surprised how little WPF takes advantages of the powerful nVidia GPU. On the nVidia Control Panel, Workstation, Manage GPU Utilization, I can monitor how many % the GPU has been tapped. During rendering, it routinely uses less than 30% during the OnRender call. After the end of the OnRender, no GPU has been use and the CPU is still clunking away data in the background trying to display. After the polygons are drawn, it becomes impossible to scroll (the control is in a ScrollViewer), change opacity, etc. These operations do not tap into any of the GPU resources either. My only solution so far is set the usercontrol's CacheMode = new BitmapCache(8).
How can I let WPF uses the GPU in this case?
for (int j = 0; j < Data.Count; j++)
{
StreamGeometry geometry = new StreamGeometry();
geometry.FillRule = FillRule.Nonzero;
using (StreamGeometryContext context = geometry.Open())
{
...
{
Point startPoint = new Point(aPolygon[0].X, aPolygon[0].Y);
List<Point> points = new List<Point>();
//draw polygons
bool isPolygonViewAble = false;
if (viewingRect.Contains(startPoint)) isPolygonViewAble = true;
for (int i = 1; i < aPolygon.Count; i++) //foreach (CADGlobal.aPoint pt in polygon)
{
Point apoint = new Point(aPolygon[i].X, aPolygon[i].Y);
if (viewingRect.Contains(apoint)) isPolygonViewAble = true;
points.Add(apoint);
}
if (isPolygonViewAble)
{
context.BeginFigure(startPoint, true, true);
context.PolyLineTo(points, true, false);
}
}
}
geometry.Freeze();
dc.DrawGeometry(null, pen, geometry); //place null with a brush to fill
Related
I am trying to create sort of a radar. Radar is VisualCollection that consists of 360 DrawingVisual's (which represent radar beams). Radar is placed on Viewbox.
class Radar : FrameworkElement
{
private VisualCollection visuals;
private Beam[] beams = new Beam[BEAM_POSITIONS_AMOUNT]; // all geometry calculation goes here
public Radar()
{
visuals = new VisualCollection(this);
for (int beamIndex = 0; beamIndex < BEAM_POSITIONS_AMOUNT; beamIndex++)
{
DrawingVisual dv = new DrawingVisual();
visuals.Add(dv);
using (DrawingContext dc = dv.RenderOpen())
{
dc.DrawGeometry(Brushes.Black, null, beams[beamIndex].Geometry);
}
}
DrawingVisual line = new DrawingVisual();
visuals.Add(line);
// DISCRETES_AMOUNT is about 500
this.Width = DISCRETES_AMOUNT * 2;
this.Height = DISCRETES_AMOUNT * 2;
}
public void Draw(int beamIndex, Brush brush)
{
using (DrawingContext dc = ((DrawingVisual)visuals[beamIndex]).RenderOpen())
{
dc.DrawGeometry(brush, null, beams[beamIndex].Geometry);
}
}
protected override Visual GetVisualChild(int index)
{
return visuals[index];
}
protected override int VisualChildrenCount
{
get { return visuals.Count; }
}
}
Each DrawingVisual has precalculated geometry for DrawingContext.DrawGeometry(brush, pen, geometry). Pen is null and brush is a LinearGradientBrush with about 500 GradientStops. The brush gets updated every few milliseconds, lets say 16 ms for this example. And that is what gets laggy. Here goes the overall logic.
In MainWindow() constructor I create the radar and start a background thread:
private Radar radar;
public MainWindow()
{
InitializeComponent();
radar = new Radar();
viewbox.Child = radar;
Thread t = new Thread(new ThreadStart(Run));
t.Start();
}
In Run() method there is an infinite loop, where random brush is generated, Dispatcher.Invoke() is called and a delay for 16 ms is set:
private int beamIndex = 0;
private Random r = new Random();
private const int turnsPerMinute = 20;
private static long delay = 60 / turnsPerMinute * 1000 / (360 / 2);
private long deltaDelay = delay;
public void Run()
{
int beginTime = Environment.TickCount;
while (true)
{
GradientStopCollection gsc = new GradientStopCollection(DISCRETES_AMOUNT);
for (int i = 1; i < Settings.DISCRETES_AMOUNT + 1; i++)
{
byte color = (byte)r.Next(255);
gsc.Add(new GradientStop(Color.FromArgb(255, 0, color, 0), (double)i / (double)DISCRETES_AMOUNT));
}
LinearGradientBrush lgb = new LinearGradientBrush(gsc);
lgb.StartPoint = Beam.GradientStarts[beamIndex];
lgb.EndPoint = Beam.GradientStops[beamIndex];
lgb.Freeze();
viewbox.Dispatcher.Invoke(new Action( () =>
{
radar.Draw(beamIndex, lgb);
}));
beamIndex++;
if (beamIndex >= BEAM_POSITIONS_AMOUNT)
{
beamIndex = 0;
}
while (Environment.TickCount - beginTime < delay) { }
delay += deltaDelay;
}
}
Every Invoke() call it performs one simple thing: dc.DrawGeometry(), which redraws the beam under current beamIndex. However, sometimes it seems, like before UI updates, radar.Draw() is called few times and instead of drawing 1 beam per 16 ms, it draws 2-4 beams per 32-64 ms. And it is disturbing. I really want to achieve smooth movement. I need one beam to get drawn per exact period of time. Not this random stuff. This is the list of what I have tried so far (nothing helped):
placing radar in Canvas;
using Task, BackgroundWorker, Timer, custom Microtimer.dll and setting different Thread Priorities;
using different ways of implementing delay: Environment.TickCount, DateTime.Now.Ticks, Stopwatch.ElapsedMilliseconds;
changing LinearGradientBrush to predefined SolidColorBrush;
using BeginInvoke() instead of Invoke() and changing Dispatcher Priorities;
using InvalidateVisuals() and ugly DoEvents();
using BitmapCache, WriteableBitmap and RenderTargetBitmap (using DrawingContext.DrawImage(bitmap);
working with 360 Polygon objects instead of 360 DrawingVisuals. This way I could avoid using Invoke() method. Polygon.FillProperty of each polygon was bound to ObservableCollection, and INotifyPropertyChanged was implemented. So simple line of code {brushCollection[beamIndex] = (new created and frozen brush)} led to polygon FillProperty update and UI was getting redrawn. But still no smooth movement;
probably there were few more little workarounds I could forget about.
What I did not try:
use tools to draw 3D (Viewport) to draw 2D radar;
...
So, this is it. I am begging for help.
EDIT: These lags are not about PC resources - without delay radar can do about 5 full circles per second (moving pretty fast). Most likely it is something about multithread/UI/Dispatcher or something else that I am yet to understand.
EDIT2: Attaching an .exe file so you could see what is actually going on: https://dl.dropboxusercontent.com/u/8761356/Radar.exe
EDIT3: DispatcherTimer(DispatcherPriority.Render) did not help aswell.
For smooth WPF animations you should make use of the
CompositionTarget.Rendering event.
No need for a thread or messing with the dispatcher. The event will automatically be fired before each new frame, similar to HTML's requestAnimationFrame().
In the event update your WPF scene and you're done!
There is a complete example available on MSDN.
You can check some graphics bottleneck using the WPF Performance Suite:
http://msdn.microsoft.com/es-es/library/aa969767(v=vs.110).aspx
Perforator is the tool that will show you performance issues. Maybe you are using a low performance VGA card?
while (Environment.TickCount - beginTime < delay) { }
delay += deltaDelay;
The sequence above blocks the thread. Use instead "await Task.Delay(...)" which doesn't block the thread like its counterpart Thread.Sleep(...).
I have a WPF Canvas that I want to make a bitmap of.
Specifically, I want to render it actual size on a 300dpi bitmap.
The "actual size" of the objects on the canvas is 10 device independent pixels = 1" in real life.
Theoretically, WPF device independent pixels are 96dpi.
I've spent days trying to get this to work and am coming up flummoxed.
My understanding is that the general procedure is roughly:
var masterBitmap = new RenderTargetBitmap((int)(canvas.ActualWidth * ?SomeFactor?),
(int)(canvas.ActualHeight * ?SomeFactor?),
BitmapDpi, BitmapDpi, PixelFormats.Default);
masterBitmap.Render(canvas);
and that I need to set the canvas's LayoutTransform to a ScaleTransform of ?SomeOtherFactor? and then do a measure and arrange of the canvas to ?SomeDesiredSize?
What I am stuck on is what to use for the values of ?SomeFactor?, ?SomeOtherFactor? and ?SomeDesiredSize? to make this work. MSDN documentation gives no indication of what factors to use.
I use this code to display images with 1:1 pixel accuracy.
double dpiXFactor, dpiYFactor;
Matrix m = PresentationSource.FromVisual(Application.Current.MainWindow).CompositionTarget.TransformToDevice;
if (m.M11 > 0 && m.M22 > 0)
{
dpiXFactor = m.M11;
dpiYFactor = m.M22;
}
else
{
// Sometimes this can return a matrix with 0s.
// Fall back to assuming normal DPI in this case.
dpiXFactor = 1;
dpiYFactor = 1;
}
double width = widthPixels / dpiXFactor;
double height = heightPixels / dpiYFactor;
Don't forget to enable UseLayoutRounding on the control as well.
I'm working on a personal project that creates an single image from a grid of images. It takes a while to generate the image and doesn't refresh everytime only once the code is done executing. How can the make the interface still functional (not locked up) when its generating the image.
So to start:
I have a N x N grid of identifiers, based on the identifier I draw a specific image at (x,y) with a given scaled height and width.
This image is regenerated each iteration and needs to be updated on the WPF. It is also bound to the ImageSource of the Image on the xaml side
My issue is 'How do I improve performance of generating this large image' and 'How do I refresh the image as many times as I need to (per generation).
for (int i = 0; i < numberOfIterations; i++)
{
// Do Some Work
UpdateImage();
}
...
BitmapImage imgFlower = new BitmapImage(new Uri(#"Images\Flower.bmp", UriKind.Relative));
BitmapImage imgPuppy = new BitmapImage(new Uri(#"Images\Puppy.bmp", UriKind.Relative));
ImageSource GeneratedImage{ get{ GenerateImage(); } set; }
...
void UpdateImage() { OnPropertyChanged("GeneratedImage"); }
...
ImageSource GenerateImage()
{
RenderTargetBitmap bmp = new RenderTargetBitmap(223, 223, 96, 96, PixelFormats.Pbgra32);
DrawingVisual drawingVisual = new DrawingVisual();
using (DrawingContext drawingContext = drawingVisual.RenderOpen())
{
double scaleRatio = CalculateScaleRatio();
DrawGridOfImages(drawingContext, scaleRatio);
}
bmp.Render(drawingVisual);
return bmp;
}
...
DrawGridOfImages(...)
{
double x,y;
for (int r = 0; r < NumberOfRows; r++)
{
x = r * scaleRatio;
for (int c = 0; c < NumberOfColumns; c++)
{
y = c * scaleRatio;
switch (imageOccupancy[r, c])
{
case Flower: drawingContext.DrawImage(imgFlower, new Rect(x,y,scaleRatio,scaleRation));
case Puppy: drawingContext.DrawImage(imgPuppy, new Rect(x,y,scaleRatio,scaleRatio));
}
}
}
}
There are two ways. To first and most beneficial would be to improve the perceived performance, do this by generating the image on a worker thread and use events to update the image on the UI thread at key points so your users can see the progress.
To improve actual performance, if you are targeting and using multicore systems you can try parallel functions if your iterations can actually be performed in parallel. This will require some work and a different mindset but will help if you put the effort in. I'd recommend studying PLINQ to get started.
Firstly: apologies if this is a duplicate post. Things got a bit confusing as I'm trying to post/register at same time.
I started investigating running UCI chess engines from a simple WPF window, got the hang of having the chess engine running onf a different thread to the interface, and have created a reasonably servcieable text-based front end.
I'm getting a bit more ambitious now, and would like to start building a GUI with chess pieces on it that will feed the player's moves to the chess engine, and represent the engine's moves on the board as well. I'm aiming for draggable pieces rather than clicking squares.
My current attempts involve using draggable user controls for the pieces on a <canvas> element. I'd be really interested to hear how other, more experienced WPF/.NET programmers would approach this, as I'm not entirely convinced I'm on the right track.
For example: would it be better to use a uniform grid and drag piece data between child elements? Should I create an abstract 'piece' class from which pieces such as pawns could derive? That kind of thing.
Any thoughts? This isn't a homework assignment or anything, just something I'm noodling around with in my spare time as an exercise.
I have implemented a chess board for my Silverlight Online Chess system.
Here is how I did it.
I made a seperate user control for the chess board
I added a grid 8x8 onto the control
I then added 64 Borders shading each one a different color (dark squares and light squares) Make sure to name each one. Each of the borders were placed on the grid using the Grid.Row and Grid.Col properties.
Within each Border I added an Image that will hold the chess piece image.
You will have to code some methods around setting the image to the correct chess piece based on your current game state.
Each of the Images received the same event (this is important), all 64 call the same piece of code:
MouseLeftButtonDown="Image_MouseLeftButtonDown"
MouseMove="Image_MouseMove"
MouseLeftButtonUp="Image_MouseLeftButtonUp"
The idea behind these 3 events is that we record when I click on the image (MouseLeftButtonDown) that gets me the origin of the click, then I call the event as the mouse is moving, that allows me to update the screen as the piece is moving, and the last event I record when I let go of the mouse button (MouseLeftButtonUp), this allows me to get the destination and send the move to my chess engine. Once the move is recorded by the chess engine I just redraw the chess board.
private void Image_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Image image = (Image)sender;
Border border = (Border)image.Parent;
image.CaptureMouse();
isMouseCapture = true;
mouseXOffset = e.GetPosition(border).X;
mouseYOffset = e.GetPosition(border).Y;
var chessPiece = (Image) sender;
var chessSquare = (Border) chessPiece.Parent;
var row = (byte) (Grid.GetRow(chessSquare));
var column = (byte) (Grid.GetColumn(chessSquare) - 1);
if (engine.HumanPlayer == ChessPieceColor.White)
{
SelectionChanged(row, column, false);
}
else
{
SelectionChanged((byte)(7 - row), (byte)(7 - column), false);
}
}
SelectionChanged is my own method for recording what source square the user selected.
isMouseCapture is also my own variable for recording when the user started draging the piece.
private void Image_MouseMove(object sender, MouseEventArgs e)
{
Image image = (Image)sender;
Border border = (Border)image.Parent;
if (!currentSource.Selected)
{
image.ReleaseMouseCapture();
isMouseCapture = false;
translateTransform = new TranslateTransform();
translateTransform.X = 0;
translateTransform.Y = 0;
mouseXOffset = 0;
mouseYOffset = 0;
}
if (isMouseCapture)
{
translateTransform = new TranslateTransform();
translateTransform.X = e.GetPosition(border).X - mouseXOffset;
translateTransform.Y = e.GetPosition(border).Y - mouseYOffset;
image.RenderTransform = translateTransform;
CalculateSquareSelected((int)translateTransform.X, (int)translateTransform.Y, false);
}
}
In the above CalculareSquareSelected converts the pixels moved to where I think the piece is moving to in the 8x8 chess board. For example say I moved 100 pixels and the chess board square is only 50 pixels than I moved 2 chess board squares.
private void Image_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (translateTransform == null)
{
return;
}
Image image = (Image)sender;
image.ReleaseMouseCapture();
isMouseCapture = false;
if (translateTransform.X > 10 || translateTransform.Y > 10 || translateTransform.X < -10 || translateTransform.Y < -10)
{
CalculateSquareSelected((int)translateTransform.X, (int)translateTransform.Y, true);
}
translateTransform = new TranslateTransform();
translateTransform.X = 0;
translateTransform.Y = 0;
mouseXOffset = 0;
mouseYOffset = 0;
image.RenderTransform = translateTransform;
}
If you have any questions feel free to contact me.
I'm trying to re-learn WPF and I've started a little project that has a line graph, kind of like the CPU performance one you would see in Taskmanager. The graph is basically a canvas that has a punch of lines added to it's children and is based on an Avalon sample I found here http://msdn.microsoft.com/en-us/library/aa480159.aspx
Currently, when I get to the far right edge of the graph I clear my data set and reset my x-coordinate back to zero and the graph starts redrawing from the left hand side after a clear.
y = value;
x = x + 1;
if (x == samples)
{
CpuGraphAnimation2d.Children.RemoveRange(0, samples);
x = 0;
}
What I want to do is have the graph 'scroll' to the left as new values arrive, and effectively have all the old values shuffle to the left and drop the left most value.
What would be the most efficient way to achieve this effect in WPF?
You could try creating a StreamGeometry as follows, removing the first element of yourarray before doing the following:
StreamGeometry sg = new StreamGeometry();
using (StreamGeometryContext context = sg.Open())
{
context.BeginFigure(new Point(0, yourarray[0]), false, false);
for (int x = 0; x < len; x++)
{
context.LineTo(new Point(x, yourarray[x], true, true);
}
sg.Freeze();
System.Windows.Shapes.Path path = new System.Windows.Shapes.Path();
path.Data = sg;
path.Stroke = Brushes.Red;
}
which is a fast way of regenerating your graph each pass. the path created at the end of the code would need to be added to a control or panel in your window.