WPF DrawingVisual performance on some machines - wpf

I am developing an application which needs to draw around 70k rectangles in form of a grid (one grid 40x250 and one 250x250). Not all rectangles will be visible on a screen at the time.
After reading http://msdn.microsoft.com/en-us/magazine/dd483292.aspx I created simple control (inherits from FrameworkElement, parts are listed below) which uses DrawingVisual descant to create one column of rectangles.
// Removing items after collection change
private void RemoveItems(IList oldItems) {
foreach (var oldItem in oldItems) {
var visualChild = _visuals.FirstOrDefault(v => v.Data == oldItem);
if (visualChild != null) {
_visuals.Remove(visualChild);
RemoveVisualChild(visualChild);
}
_translateTransform.X -= 8;
}
}
// Adding items after collection change
private void AddItems(IList newItems) {
foreach (var newItem in newItems) {
var newArray = (byte[]) newItem;
var visual = CreateVisual(newArray);
_visuals.Add(visual);
AddVisualChild(visual);
_col++;
}
}
// Creating visual element
private StripeVisual CreateVisual(byte[] data) {
var result = new StripeVisual {
Data = data,
Transform = _translateTransform
};
using (var dc = result.RenderOpen()) {
for (int i = 0; i < data.Length; i++) {
dc.DrawRectangle(_brushes[data[i]], _strokePen, new Rect(_col * 8, i * 20, 8, 20));
}
}
return result;
}
All brushes are frozen (members of Brushes class) and _strokePen is frozen. New vertical stripe is added to collection every 200ms.
Program has been run on 3 computers:
Notebook: i7-2670QM, GeForce GT 540M/Intel HD Graphics 3000, 4GB RAM, Win7 x64, DirectX11,
PC: Core2Duo E7400, Radeon HD 4800, 3GB RAM, Win7 x64, DirectX11,
PC: i3 3,07GHz, Inter HD Graphics, 3.3GB RAM, Win7 x86, DirectX11.
The problem is that on my first computer program runs smoothly and without big impact on CPU (usage between 1-10%). On both second and third computers programs performance is unacceptable: with about 30x100 grid it takes up to 50% of processor and everything runs very slow. Program runs slowly even when only small part of grid is visible.
It looks like program is using software rendering (rendering thread is using 50% of processor), but RenderCapability.Tier >> 16 returns 2, and RenderOption.ProcessRenderMode is set to Default. Also there is no key in [HKCU/Software/Microsoft/Avalon.Graphics] that would disable hardware acceleration.
Is it possible to run this application smoothly listed computers? What else can I do to maximize performance of this program?
Update
Setting DrawingVisual.CacheMode = new BitmapCache(); helped a lot. Does it mean that CPU<=>GPU communication was the bottleneck?

Related

GetWindowRect returning wrong coordinaties

i'm developing VSTO add-in for outlook which includes overlay on top of the window.
I'm building my UI using WPF.
Problem is that when i'm trying to attach WPF Window ( merge left/top/width/height ) to outlook window when STARTING at scale more than 100% GetWindowsRect Returns wrong rectangle.
BUT when i'm starting application at 100% scale then change windows scale at runtime to whatever value everything is good and DPI Aware. Both cases ( starting and runtime ) GetDpiForWindow returns correct values which is...strange. DPI Awareness is set using SetThreadDpiAwareness when forms are created.
Can't get my head what's wrong :<. Any advises appreciated.
Code for attaching:
public void AttachTo(IntPtr src, AttachFlagEnum flags)
{
var nativeRectangle = new WinAPI.RECT();
if (!WinAPI.GetWindowRect(src, ref nativeRectangle))
{
// throw new Win32Exception(Marshal.GetLastWin32Error());
return;
}
AttachToCoords(new Rectangle(nativeRectangle.Left, nativeRectangle.Top, nativeRectangle.Right - nativeRectangle.Left, nativeRectangle.Bottom - nativeRectangle.Top), flags);
}
Form create code:
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
StateManager.Init();
OutlookUtils.WaitOutlookLoading();
using (var ctx = new DPIContextBlock(WinAPI.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE))
{
new Forms.One().Show();
new Forms.Overlay().Show();
new Forms.Two();
}
}
Overlay attach code (executes by timer )
private void OverlayThink(object ob)
{
if (Managers.StateManager.OutlookState == OutlookStateEnum.MINIMIZED || Managers.StateManager.UiState == UIStateEnum.DESCWND)
{
if (this.IsVisible)
{
this.Dispatcher.Invoke(() => this.Hide());
}
return;
}
this.Dispatcher.Invoke(() => this.AttachTo(Utils.OutlookUtils.GetWordWindow(), AttachFlagEnum.OVERLAY));
this.Dispatcher.Invoke(() => this.Show());
}
My solution to the problem was that AttachToCoords method sets coords from GetWindowRect directly to Window.Left | Right | Top | Bottom. That's wrong because internally WPF positions it's elements in 96 DPI coordinate system. So i was need to convert it before assigning.
Solution:
private Rectangle TransformCoords(Rectangle coords)
{
var source = PresentationSource.FromVisual(this);
coords.X = (int)(coords.X / source.CompositionTarget.TransformToDevice.M11);
coords.Y = (int)(coords.Y / source.CompositionTarget.TransformToDevice.M22);
coords.Width = (int)(coords.Width / source.CompositionTarget.TransformToDevice.M11);
coords.Height = (int)(coords.Height / source.CompositionTarget.TransformToDevice.M22);
return coords;
}
WPF (as well as Windows forms) should be scaled automatically depending on the DPI value set on the system. There is no need to calculate the size and positions of the dialog window in Outlook add-ins.
Instead, you need to set up the form correctly to follow the DPI settings and set the window parent, so it will be displayed on top of the Outlook window.

WPF, rendering a million polygons, nVidia Quardro K2200

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

SharpDX.WPF Increasing memory usage during rendering

I have started working with DirectX in WPF app. My first step was to use simple library:
SharpdDX.WPF. Based on samples I've implemented WPF control drawing simple line. SharpDX.WPF uses D3DImage to render images in WPF.
Unfortunately application's memory increasing all time.
I implemented class TestControlRenderer : D3D10.
Vertex shader is initialized like:
var sizeInBytes = dataLength * sizeof(int) * 3;
var bufferDescription = new BufferDescription(
sizeInBytes,
ResourceUsage.Dynamic,
BindFlags.VertexBuffer,
CpuAccessFlags.Write,
ResourceOptionFlags.None);
using (var stream = new DataStream(sizeInBytes, true, true))
{
stream.Position = 0;
_graphDataVertexBuffer = new SharpDX.Direct3D10.Buffer(Device, stream, bufferDescription);
}
Device.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(_graphDataVertexBuffer, sizeof(int) * 3, 0));
Device.InputAssembler.PrimitiveTopology = PrimitiveTopology.LineStrip;
Then constant buffer with parameters used in shader:
_controlInfoConstantBuffer = new ConstantBuffer<ControlParamsShaderData>(Device);
Device.VertexShader.SetConstantBuffer(0, _controlInfoConstantBuffer.Buffer);
To init animation Reset method was overriden like that:
base.Reset(args);
if (args.RenderSize.Width == 0) return;
_drawArgs = args;
InitVertexBuffer(dataLength);
_controlInfoConstantBuffer.Value = new ControlParamsShaderData
{
SamplesInControl = dataLength,
MinSignalDataY = -1500,
MaxSignalDataY = 1500
};
Device.VertexShader.SetConstantBuffer(0, _controlInfoConstantBuffer.Buffer);
The last step is RenderScene method:
public override void RenderScene(DrawEventArgs args)
{
if (args.RenderSize.Width == 0) return;
Device.ClearRenderTargetView(RenderTargetView, Color.Transparent);
using (var stream = _graphDataVertexBuffer.Map(MapMode.WriteDiscard, SharpDX.Direct3D10.MapFlags.None))
{
for (int i = 0; i < Data.Length; i++)
{
stream.Write(new Vector3(i, Data[i], 0));
}
}
_graphDataVertexBuffer.Unmap();
Device.Draw(Data.Length, 0);
}
Rendering is controlled by DispatcherTimer where OnTickMethod updates array with points coordinates and then invoke Render() method.
My question is simply, is that memory leak or something is created on each render iteration?
I don't change backbuffer or create another objects. Only change Data array, update it to GPU and Shaders process it to display.
My case is to display about 30 wpf controls width DirectX on one screen. Controls are with simple but realtime animation. Is that possible in that way?
Most likely you are leaking resources. You can see this by setting the static configuration property
SharpDX.Configuration.EnableObjectTracking = true;
then calling
SharpDX.Diagnostics.ObjectTracker.ReportActiveObjects()
at various points in your application lifetime to see if anything is leaking (at least on the SharpDX side). You can edit your code to make sure to dispose these objects. Only enable object tracking while debugging - it hurts performance.
SharpDX used to release COM objects when the finalizer ran if the object had not already been Diposed (at least as in version 2.4.2), but later disabled that (they detail why in one of their changelogs, I forget which one).
Additionally, DirectX requires that you release objects in the reverse order they were created - this can create hard-to-debug memory leaks. So when your code is
var device = new Devie(...);
var effect = new Effec(Device, byteCode);
technique = effect.GetTechniqueByName(techniqueName);
inputLayout = new InputLayout(Device, _technique.GetPassByIndex(0).Description.Signature, ...);
then your dispose code has to be
_inputLayout.Dispose();
_technique.Dispose();
_effect.Dispose();
_device.Dispose();

How to achieve smooth UI updates every 16 ms?

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(...).

WPF Image Generation using an N x N grid of images

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.

Resources