Data binding performance issues - wpf

I am writing a map control that can display a bench of geometries. For better performance, I draw all my geometries using DrawingVisuals which I then write into a RenderTargetBitmap as shown in the code below:
public class Map{
public ImageSource MapDrawingImage{get;set;}
private void RenderMap(){
MapDrawingImage= new RenderTargetBitmap(w, h, 96, 96, PixelFormats.Pbgra32);
foreach (Layer layer in map.Layers) {
System.Windows.Media.DrawingVisual layerDrawing = Render(layer, map);
MapDrawingImage.Render(layerDrawing);
}
}
}
In order to display the map, the main window has an Image control which Source is set to Map.MapDrawingImage image source. To automatically update the image, I use the following data binding:
RenderOptions.SetBitmapScalingMode(mapImage, BitmapScalingMode.LowQuality);
// Map image binding
Binding mapBinding = new Binding();
mapBinding.Source = map;
mapBinding.Path = new PropertyPath("MapDrawingImage");
mapImage.SetBinding(System.Windows.Controls.Image.SourceProperty, mapBinding);
This works very well when the map is static. However, in a dynamic mode where the map is updated at a rate of 5 to 10 times a second, the data binding seems to fall a bit short behind and the application slows down. I have searched for long days and I found out that:
RenderTargetBitmap does not use hardware acceleration thus causing some delays when rendering the map.
Data binding might also cause some delays refreshing the map image
Is there any better way to improve the map performance (RenderTargetBitmap replacement, data binding improvement) ?
Thanks in advance,

Databinding is updated in a seperate thread. So this will always be with a delay. Also it works with a queue, so when the databinding can't keep up the queue will grow bigger and bigger. The solution would be that you use an image which is static and change the image itself instead of replacing it with another image.

Related

Is there a way to move some images on the screen and while they are moving record the scene as a video?

as you can read from the title i want to be able to draw some images on the screen, move them in some direction and video capture the movement with a good fps rate.
I want to specify that i do not want to record the desktop nor some portion of it but the content of the actual window in which the images are moving(so the window can also be minimized). Also if possible i want to be able to set a custom size for my view where everything will happen.
Where i should start from?
I have already tried with WPF but as the UI is single threaded i am not able to take a screenshot of the view while something is moving on it.
What library you would suggest me?
Are there similar open-source projects i can learn from?
Any suggestion i welcomed!
Here's some code I recently wrote to do this, it cycles through a number of frames and renders a control (in this case, a Canvas) into PNGs:
private void Export(int frame)
{
// force the control to update after any changes you've just made
theCanvas.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate);
// render the control into a bitmap
RenderTargetBitmap bitmap = new RenderTargetBitmap(1920, 1080, 96, 96, PixelFormats.Pbgra32);
bitmap.Render(theCanvas);
// save the bitmap out as a PNG
using (var stream = File.Create($"Animation/Frame_{frame.ToString("D3")}.png"))
{
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmap));
encoder.Save(stream);
}
}
You can then use ffmpeg to pack those PNGs into the movie file format of your choice.

WPF RenderTargetBitmap Missing Elements

I have a TreeView with small icons displayed in the data template. I'm trying to save the Treeview as a PNG using RenderTargetBitmap.
The image saves correctly on small data sets. However, if the data set becomes too large, some of the icons are excluded from the final image. The magic number seems to be 200 items. It doesn't seem to matter if the tree is deep or wide, after 200 items, the icons are not rendered.
Added Code
So here is my code that I'm using to create an image.
RenderTargetBitmap targetBitmap = new RenderTargetBitmap(
(int)_treeView.ActualWidth,
(int)_treeView.ActualHeight,
96, 96, PixelFormats.Default);
targetBitmap.Render(_treeView);
Added Screen Shot
Notice the missing icons way over on the right side of the tree.
Now if I collapse a few branches, thus hiding some of the other icons, then these icons are included. It's almost like RenderTargetBitmap.Render doesn't have the power to render all of the icons. Or it may have something to do with virtual panels.
Here is a closer look.
What I immediately noticed that you have HUGE image. Width 12000. I am surprised that you even got that close.
As MSDN states, the texture width/height are limited by DirectX texture limits.
The maximum rendered size of a XAML visual tree is restricted by the maximum dimensions of a Microsoft DirectX texture; for more info see Resource Limits (Direct3D). This limit can vary depending on the hardware whre the app runs. Very large content that exceeds this limit might be scaled to fit. If scaling limits are applied in this way, the rendered size after scaling can be queried using the PixelWidth and PixelHeight properties. For example, a 10000 by 10000 pixel XAML visual tree might be scaled to 4096 by 4096 pixels, an example of a particular limit as forced by the hardware where the app runs.
http://msdn.microsoft.com/library/windows/apps/dn298548
I suspect these things:
Virtualization cutting off some things - I've had the exact problem in past with DataGrid, and the problem was virtualization. Your case doesn't seem like one though.
Too big texture can cause undefined behaviour.
You can try disabling hardware acceleration. The thing causes quite few hardcore bugs. http://msdn.microsoft.com/en-us/library/system.windows.media.renderoptions.processrendermode.aspx
Other than that - it will be tricky, but I am pretty sure that it will work beautifully:
1) start with the root object, and traverse the root object childrens recursively, until you find an object that is less than 1000 x 1000. Take picture of it using RenderTargetBitmap(BMP) and merge it to IN-MEMORY-BMP. Do it for each children.
You should be able to calculate all this stuff.
For the records: there's a workaround.
Instead of rendering your Visual directly with RenderTargetBitmap, use an interim DrawingVisual. Paint your Visual into the DrawingVisual using a VisualBrush and then use RenderTargetBitmap with the DrawingVisual.
Like this:
public BitmapSource RenderVisualToBitmap(Visual visual)
{
var contentBounds = VisualTreeHelper.GetContentBounds(visual);
var drawingVisual = new DrawingVisual();
using (var drawingContext = drawingVisual.RenderOpen())
{
var visualBrush = new VisualBrush(visual);
drawingContext.DrawRectangle(visualBrush, null, contentBounds);
}
var renderTargetBitmap = new RenderTargetBitmap((int)contentBounds.Width, (int)contentBounds.Height, 96, 96, PixelFormats.Default);
renderTargetBitmap.Render(drawingVisual);
return renderTargetBitmap;
}
Note however that as your VisualBrush gets bigger the resulting image gets more and more fuzzy (when rendering with high DPI). To work around this problem use a series of smaller VisualBrush "tiles" as described here:
https://srndolha.wordpress.com/2012/10/16/exported-drawingvisual-quality-when-using-visualbrush/

How to smooth WPF animation?

I am struggling in smoothing WPF animation
Actually my animation code is as follows:
private void AnimateX ( FrameworkElement element, double XMoveStart, double XMoveEnd, int secondX)
{
SineEase eEase = new SineEase();
eEase.EasingMode = EasingMode.EaseInOut;
Storyboard sb = new Storyboard();
DoubleAnimation daX = new DoubleAnimation(XMoveStart, XMoveEnd, new Duration(new TimeSpan(0, 0, 0, secondX, 0)));
daX.EasingFunction = eEase;
Storyboard.SetTargetProperty(daX, new PropertyPath("(Canvas.Left)"));
sb.Children.Add(daX);
element.BeginStoryboard(sb);
}
The above code is a method to move an object horizontally with sine ease. When only one object is moving, it is OK. However, whenever two or more objects move together (call AnimateX method on another object when the previous animation has not yet completed), the animation starts to become jittery. By jittery I mean, the objects are kind of shaking during the course of animation.
I faced the same problem many times. I found out that depending on the objects you add to your canvas, WPF will often have to regenerate representations of these objects on every frame (which I believe might be your case, depending on the type of UI elements you are manipulating). You can solve the jitter issue by telling WPF to cache a representation of your canvas in a bitmap. This is done very simply as follows, in your Xaml definition of the canvas:
<Canvas ...Your canvas properties...>
<Canvas.CacheMode>
<BitmapCache />
</Canvas.CacheMode>
...Your objects...
</Canvas>`
This reduces the load on your WPF application, as it simply stores the representation of your objects as a bitmap image, and as a consequence your application does not have to redraw them on every frame. This solution only works if your animation is applied externally to the canvas, and that there is no on-going local animations applying to the individual objects drawn in your canvas. You'll want to create separates canvases with their own caching if other animations in your code move the two objects with respect to each other.
Note that some UI elements will not be eased by this strategy. However, I've seen this strategy work efficiently for many elements, including TextBoxes and the likes, as well as geometric shapes. In any case, it's always worth the try.
Secondly, if caching local representations does not suffice, then you might want to have a look at the performance of your code and see if any process could be responsible for blocking the UI momentarily. There is no uniform solution regarding this aspect and it depends on what else is putting strain on your application UI. Cleaning the code and using asynchronous processes where relevant could help.
Finally, if, after all these checks the overall demand on your application remains too high, you can somewhat remove some strain on the application by reducing its general frame rate, the default being 60. You can try 30 or 40 and see if this improves the jittering by including the following code in your initialization:
Timeline.DesiredFrameRateProperty.OverrideMetadata(typeof(Timeline), new FrameworkPropertyMetadata { DefaultValue = 40 });
Just a guess, but what happens if you directly animate the property, withoud using a Storyboard?
private void AnimateX(FrameworkElement element, double xMoveStart, double xMoveEnd, double durationSeconds)
{
DoubleAnimation animation = new DoubleAnimation
{
From = xMoveStart,
To = xMoveEnd,
Duration = TimeSpan.FromSeconds(durationSeconds),
EasingFunction = new SineEase { EasingMode = EasingMode.EaseInOut }
};
element.BeginAnimation(Canvas.LeftProperty, animation);
}

How to improve Canvas rendering performance?

I have to draw a lot of Shape (about 1/2 hundred thousand) as [Canvas][2]'s childrens. I make this in my WPF application dividing work in two parts: first thing I create shapes by setting the properties of each of them (like Margin, Fill, Width, etc...), after I add shapes as Canvas's children.
MyCanvas.Children.Add(MyShape)
Now i want to improve the performance of the second part, because when i draw the shapes my application is blocked for a long period of time. So i tried to use the Dispatcher and its method [BeginInvoke][4] with different [priorities][5]: only if I use the Background priority the main application does not block, otherwise the application remains blocked and the "picture" is not displayed until all shapes are added to my Canvas, but if I use the Background priority obviously everything is slower. I also tried to create a new thread instead of using the Dispatcher, but there was no significant change.
How can I fix this problem, and generally improve the performance of my application when I add my shapes to Canvas?
Thanks.
Need to use Visual objects instead of Shape; in particular, as suggested, DrawingVisual: a visual object that can be used to render vector graphics. In fact, as written in the MSDN library:
DrawingVisual is a lightweight drawing class that is used to render shapes, images, or text. This class is considered lightweight because it does not provide layout, input, focus, or event handling, which improves its performance. For this reason, drawings are ideal for backgrounds and clip art.
So, for example, to create a DrawingVisual that contains a rectangle:
private DrawingVisual CreateDrawingVisualRectangle()
{
DrawingVisual drawingVisual = new DrawingVisual();
// Retrieve the DrawingContext in order to create new drawing content.
DrawingContext drawingContext = drawingVisual.RenderOpen();
// Create a rectangle and draw it in the DrawingContext.
Rect rect = new Rect(new System.Windows.Point(160, 100), new System.Windows.Size(320, 80));
drawingContext.DrawRectangle(System.Windows.Media.Brushes.LightBlue, (System.Windows.Media.Pen)null, rect);
// Persist the drawing content.
drawingContext.Close();
return drawingVisual;
}
In order to use DrawingVisual objects, you need to create a host container for the objects. The host container object must derive from the FrameworkElement class, which provides the layout and event handling support that the DrawingVisual class lacks. When you create a host container object for visual objects, you need to store the visual object references in a VisualCollection.
public class MyVisualHost : FrameworkElement
{
// Create a collection of child visual objects.
private VisualCollection _children;
public MyVisualHost()
{
_children = new VisualCollection(this);
_children.Add(CreateDrawingVisualRectangle());
// Add the event handler for MouseLeftButtonUp.
this.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(MyVisualHost_MouseLeftButtonUp);
}
}
The event handling routine can then implement hit testing by invoking the HitTest method. The method's HitTestResultCallback parameter refers to a user-defined procedure that you can use to determine the resulting action of a hit test.
Agreed that if you want to draw millions of elements, you simply can't do it in WPF. WriteableBitmapEx as mentioned is a good alternative.
See this related question which goes into depth on high performance graphics in WPF and the alternatives available.
If you simply must use Canvas, check out this ZoomableApplication2 - A million items. This is a Canvas based demo which makes heavy use of Virtualization to get reasonable performance with 1,000,000 UIElements on a Canvas.
That's a lot of UIElements and probably isn't going to give the kind of performance you're looking for. Do you need to be able to interact with each of the elements you're rendering? If not, I would highly recommend looking into using WriteableBitmap instead. If you need to draw shapes and don't want to create all that logic yourself (who would want to?), check out the WriteableBitmapEx project over on CodePlex.
This may be somewhat unrelated, and I apologize if you feel this way, but in the hopes that it can shed some light for other users, I'll share this tidbit.
We had some performance issues with a Canvas control used for capturing signatures. The capture was very jagged, and we couldn't draw curved lines as a result. It turned out to be related to a style was was generating drop-shadows on the UI elements. Disabling the drop-shadow effect solved our problem.

Releasing BitmapImages used as Image control Source (memory problem)

I have a simple WPF window with an Image control and a Slider control under it. When the user moves the slider, I'm loading and setting various images as Source of the Image-control. I got quite a few images, varying in size up to a 200 KB, but when I move that slider back and fourth, the program starts to eat quite a lot of memory. Hundreds and hundreds of megs of memory. Most of it gets garbage collected eventyally, but not all.
Maybe WPF isn't the way to go or should I force a G/C? I've tried loading the image as Bitmap and getting the bitmap source with Imaging.CreateBitmapSourceFromHBitmap() and Win32-api's to delete and dispose and so on, but I'm just making things worse :)
I guess I should try to grab the existing image source and release it somehow before loading and assigning a new one.
Any ideas?
EDIT
I'm adding some sample code that worked fine and seems to keep the memory low and fine:
private Image _lastImage;
// Event when user moves the slider control, load image using the filname in
// the _images[] array which contains hundreds of images
private void SliderChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (_lastImage != null)
_lastImage.Dispose();
var image = Image.FromFile(_images[(int)ImageSlider.Value]);
Snapshot.Source = ImageToBitmapImage(image);
_lastImage = image;
}
private static ImageSource ImageToBitmapImage(Image image)
{
BitmapImage bitmapImage;
using (var ms = new MemoryStream())
{
image.Save(ms, ImageFormat.Jpeg);
bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = new MemoryStream(ms.ToArray());
bitmapImage.EndInit();
}
return bitmapImage;
}
Forcing a GC collection is indeed horrible. Instead call the .Dispose() method of the Bitmap object, that's what it's there for!
The method you are using (Imaging.CreateBitmapSourceFromHBitmap()) is unmanaged and probably requires that you manually .Dispose() of it.
WPF allows you to load a bitmap via a given URI : see http://msdn.microsoft.com/en-us/library/system.windows.media.imaging.bitmapimage.aspx
The GC will take care of collecting any managed objects you have (by using BitmapImage for example).
You are probably loading too many images to memory. the source images accumulate memory pretty fast (1024x786 image at 32bit color takes up around 3mb of ram). The GC has to work very hard to clean them up. You could reduce the size of the image after loading (scale it down) and use up less memory
Finally, the GC does not guarantee that all memory will be released after the GC cycle.
It promises "best effort". although typically calling GC.Collect(0); will force a full GC cycle and release almost everything that can be released.

Resources