I plot animated graph with Dynamic Data Display - graph that has source with constantly adding new points (about 10 times per second and 10 points per time). The problem is the performance degrade if these points form line segments which are big in compare to plotter size. In other words I can add thousands of points but if they forms comparably small line segments the performance will be good. But if I add only few hundreds points which form big line segments (they occupy all the plotter's area) the performance will be very poor.
The following images illustrate the problem. The first image is just hundreds line segments and performance is very poor on my machine. In the second image I just add two points to make the rest points took small place on the plotter due to plotter's scaling.
It doesn't matter how fast your machine is (mine is Pentium G3220 with integrated GPU, 16 GB RAM, and another one is relatively same CPU with discrete GeForce 640 GT) - I can add much more points and will fall your machine down. The behavior will be the same - if I add two "defend scaling" points and make the line segments relatively small on the plotter the performance will be much better.
The following code - how I get such pictures. Leave the adding of two points in MainWindow() commented to obtain the first picture, and uncomment it to obtain the second one. This is just a plane WPF application with all code in one file - in MainWindow's "code behind":
public partial class MainWindow : Window
{
readonly DispatcherTimer _timer = new DispatcherTimer();
int _counter = 0;
int _batchSize= 10;
RingArray<Point> _data = new RingArray<Point>(2000);
EnumerableDataSource<Point> _ds;
public MainWindow()
{
InitializeComponent();
_ds = new EnumerableDataSource<Point>(_data);
_ds.SetXMapping(d => d.X);
_ds.SetYMapping(d => d.Y);
LineGraph chart = plotter.AddLineGraph(_ds, Colors.Blue, 2.0);
//_data.Add(
// new Point
// {
// X = -1,
// Y = 200
// });
//_data.Add(
// new Point
// {
// X = -0.5,
// Y = -200
// });
_timer.Tick += OnTimerTick;
_timer.Interval = TimeSpan.FromMilliseconds(100);
_timer.Start();
}
void OnTimerTick(object sender, EventArgs e)
{
_data.AddMany(
Enumerable.Range(0, _batchSize)
.Select(v =>
{
// I don't use the "v" here - the "_counter" is enough.
var p = new Point
{
X = (double)_counter,
Y = (double)(_counter % 2 == 0 ? 0 : 1)
};
_counter++;
return p;
}));
}
}
public class Point
{
public double X { get; set; }
public double Y { get; set; }
}
It doesn't matter whether I use EnumerableDataSource or ObservableDataSource with AppendAsync, Add or AddMany - it is all the same behavior: the performance depends only on the size of line segments relatively to screen size (or plotter size). The problem lays definitely in the field of 1) WPF's rendering system, and 2) rasterization.
1) bad WPF's rendering system https://jeremiahmorrill.wordpress.com/2011/02/14/a-critical-deep-dive-into-the-wpf-rendering-system/
2) rasterization - the more pixels the line segments occupy on the screen the less performance will be... but dude, several hundreds lines fall down my machine but Crysis on max settings not! Ridiculous!
I know the following approaches to cope with this problem:
reduce the size of the plotter (but in my case it have to be as large as possible because it is the main part of the user interface and it is dedicated for monitoring purposes);
reduce the size of line segments (not in my case because the data must be displayed as they are);
apply a filter to the data like plotting only each tenth point instead of every first one - already done but it has limited application (my graph looks too jugged);
What else would you offer?
Related
How is the approach to plot complex drawings with Direct2D (Sharpdx)?
Actually I am using a WindowsRenderTarget, connecting it with a Direct2D1.Factory and drawing to a RenderControl.
Factory2D = new SharpDX.Direct2D1.Factory(FactoryType.MultiThreaded);
FactoryWrite = new SharpDX.DirectWrite.Factory();
var properties = new HwndRenderTargetProperties();
properties.Hwnd = this.Handle;
properties.PixelSize = new Size2(this.ClientSize.Width, this.ClientSize.Height);
properties.PresentOptions = PresentOptions.RetainContents;
RenderTarget2D = new WindowRenderTarget(Factory2D, new RenderTargetProperties(new PixelFormat(Format.Unknown, AlphaMode.Premultiplied)), properties);
RenderTarget2D.AntialiasMode = AntialiasMode.PerPrimitive;
The drawing is done in the Paint Event of the form:
RenderTarget2D.BeginDraw();
RenderTarget2D.Clear(Color4.Black);
drawProgress(); // Doing Paintings like DrawLine, Multiple PathGeometrys, DrawEllipse and DrawText
RenderTarget2d.EndDraw();
In the MouseMove/MouseWheel event the drawing will be recalculated (for scaling or calculation of the elements that will be displayed). This process need about 8-10ms.
The next step is actually
this.Refresh();
Here, I guess, is the problem, this progress needs up to 140ms.
So the scaling/moving of the plot has about 7fps.
Also the program occupies more and more memory when refreshing the Control
////Edit
Painting of lines:
private void drawLines(Pen pen, PointF[] drawElements)
{
SolidColorBrush tempBrush = new SolidColorBrush(RenderTarget2D, SharpDX.Color.FromRgba(pen.Color.ToArgb()));
int countDrawing = (drawElements.Length / 2) + drawElements.Length % 2;
for (int i = 0; i < countDrawing; i++)
{
drawLine(new Vector2(drawElements[i].X, drawElements[i].Y), new Vector2(drawElements[i + 1].X, drawElements[i + 1].Y), brushWhite);
}
}
Painting geometrys:
RenderTarget2D.DrawGeometry(graphicPathToPathGeometry(p), penToSolidColorBrush(pen));
private PathGeometry graphicPathToPathGeometry(GraphicsPath path)
{
geometry = new PathGeometry(Factory2D);
sink = geometry.Open();
if (path.PointCount > 0)
{
sink.BeginFigure(new Vector2(path.PathPoints[path.PointCount - 1].X, path.PathPoints[path.PointCount - 1].Y), FigureBegin.Hollow);
sink.AddLines(pointFToVector2(path.PathPoints));
sink.EndFigure(new FigureEnd());
sink.Close();
}
return geometry;
}
In mouse move the drawing will be recalculated by just building differences between Cursor.Position.X/Y old and Cursor.Position.X/Y new. So the the lines will be recalculated really often :)
The main bottleneck is your graphicPathToPathGeometry() function. You are creating and "filling" a PathGeometry in a render loop. As I mentioned above, a core principle is that you have to create your resources at once and then just reuse them in your drawing routine(s).
About your memory leak... your code samples don't provide enough information, but most probably you are not freeing the resources that you are creating (ie PathGeometry, SolidColorBrush and the ones we don't see).
The simplest advise is - use your render loop only for rendering/drawing and reuse resources instead of recreating them.
Improving the performance of Direct2D apps
One part of the problem is:
SolidColorBrush tempBrush = new SolidColorBrush(RenderTarget2D, SharpDX.Color.FromRgba(pen.Color.ToArgb()));
Creating objects of any kind inside the renderloop creates a big memory lack in the application. Drawing existing values is the way to go.
I guess the performance issue will also be based on this problem.
I profiled my application (a game), and I noticed that this function is a (the!) bottleneck. In particular, this function is called a lot due to it's meaning: it draws windows of my game's skyscraper. The game is flowing horizontally so, everytime new skyscraper are generated and windows has to be drawn.
The method is simple: I load just one image of a window and then I use it like a "stencil" to draw every window, while I calculate its position on the skyscraper.
position_ is the starting position, based on top-left corner where I want to begin drawing
n_horizontal_windows_ and n_vertical_windows_ is self-explanatory and it is generated in constructor
skipped_lights_ is a matrix of bool that says if that particular light is on or off (off means don't draw the window)
delta_x is like the padding, the distance between a window and another.
w_window is the width of the window (every window has the same)
public override void Draw(SpriteBatch spriteBatch)
{
Vector2 tmp_pos = position_;
float default_pos_y = tmp_pos.Y;
for (int r = 0; r < n_horizontal_windows_; ++r)
{
for (int c = 0; c < n_vertical_windows; ++c)
{
if (skipped_lights_[r, c])
{
spriteBatch.Draw(
window_texture_,
tmp_pos,
overlay_color_);
}
tmp_pos.Y += delta_y_;
}
tmp_pos.X += delta_x_ + w_window_;
tmp_pos.Y = default_pos_y;
}
}
As you can see the position is calculated inside the loop.
Just an example of the result (as you can see I create three layers of skyscrapers):
How can I optimize this function?
You could always render each building to a texture and cache it while it's on screen. That way you only draw the windows once for each building. you will draw the entire building in one call after it's been cached, saving you from building it piece by piece every frame. It should prevent a lot of over-draw that you were getting each frame too. It will have a slight memory cost though.
So, I have a rectangle "rectangle1", at 160,160.
I want it to move smoothly to cordinates 160,30, with a duration of about 1 second. (time delay)
I've figured out that some basic code to move the shape is
rectangle1.Location = new Point(160,30);
However, when I tried doing a for loop with
rectangle1.Location = new Point(160, rectangle1.Location.Y - 100);
it just moved there instantly. Which I should have expected really. Same occurred with
int count = 0;
while(count != 300)
{
rectangle1.Location = new Point(160, rectangle1.Location.Y -1);
count += 2;
}
So, I assume I need some sort of clock / timer loop, that moves it by x pixels every x milliseconds. Not sure how to do this, so help would be appreciated.
Also, I'm going to be animating two other rectangles horizontally, which will then move up at the same time/speed as rectangle1. I think I'll have to "delay" rectangle1's movement until they are in position, correct?
Thanks.
PS: I've googled a fair bit, but since I'm not entirely sure what I'm looking for, it wasn't very fruitful.
If you need smooth movements, it's great to use timers, threads, backgroundworkers.
Here is what you need to do. Assuming you have the code that increment/decrement x,y points for the shape.
Steps:
set timer interval to for e.g. 100
set an integer int count=0; *
in timer_tick event do the moving work
private void timer1_Tick(object sender, EventArgs e)
// no need to use your while loop anymore :))
{
If(count< 300) //set to your own criteria
{
//e.g. myrect.location=new point(x,y);
// rectangle1.Location = new Point(160, rectangle1.Location.Y -1);
}
count += 2;
}
First time I ever ask a question here so correct me if I´m doing it wrong.
Picture of my chess set:
Every time I move a piece it lags for about 1 second. Every piece and tile has an Image and there is exactly 96 Images. Every time I move a piece it clears everything with black and then update the graphics.
In the early stages of the chess I didn't have any Images and used different colors instead and only a few pieces there was no noticeable lag and the piece moved in an instant.
public void updateGraphics(PaintEventArgs e, Graphics g, Bitmap frame)
{
g = Graphics.FromImage(frame);
g.Clear(Color.Black);
colorMap(g);
g.Dispose();
e.Graphics.DrawImageUnscaled(frame, 0, 0);
}
The function colorMap(g) looks like this:
private void colorMap(Graphics g)
{
for (int y = 0; y < SomeInts.amount; y++)
{
for (int x = 0; x < SomeInts.amount; x++)
{
//Tiles
Bundle.tile[x, y].colorBody(g, x, y);
//Pieces
player1.colorAll(g);
player2.colorAll(g);
}
}
}
The colorAll function executes every pieces colorBody(g) function which look like this:
public void colorBody(Graphics g)
{
//base.colorBody() does the following: body = new Rectangle(x * SomeInts.size + SomeInts.size / 4, y * SomeInts.size + SomeInts.size / 4, size, size);
base.colorBody();
if (team == 1)
{
//If its a white queen
image = Image.FromFile("textures/piece/white/queen.png");
}
if (team == 2)
{
//If its a black queen
image = Image.FromFile("textures/piece/black/queen.png");
}
g.DrawImage(image, body);
}
and finaly the function that moves the piece:
public void movePiece(MouseEventArgs e)
{
for (int y = 0; y < SomeInts.amount; y++)
{
for (int x = 0; x < SomeInts.amount; x++)
{
if (Bundle.tile[x, y].body.Contains(e.Location))
{
//Ignore this
for (int i = 0; i < queens.Count; i++)
{
Queen temp = queens.ElementAt<Queen>(i);
temp.move(x, y);
}
//Relevant
player1.move(x, y);
player2.move(x, y);
}
}
}
}
Thank you for reading all this! I could make a link to the whole program if my coding examples is not enough.
You're calling Image.FromFile in every refresh, for every image - effectively reloading every image file every time from disk.
Have you considered loading the images once, and storing the resulting Images somewhere useful? (say, an array, Image[2,6] would be adequate)
Why do you redraw the board each time? Can't you just leave the board where it is and display an image with transparent background over it? That way you have one image as a background (the board), plus 64 smaller images placed over the board in a grid and just change the image being displayed on each move.
That way, you can let windows handle the drawing...
Also, load the images of the pieces at the start of the application.
In addition to not calling Image.FromFile() inside updateGraphics() (which is definitely your biggest issue), you shouldn't be attempting to redraw the entire board every on every call to updateGraphics() - most of the time, only a small portion of the board will be invalidated.
The PaintEventArgs contains an parameter, ClipRectangle, which specifies which portion of the board needs redrawing. See if you can't figure out which tiles intersect with that rectangle, and only redraw those tiles :)
Hint: Write a function Point ScreenToTileCoords(Point) which takes a screen coordinate and returns which board-tile is at that coordinate. Then the only tiles you need to redraw are
Point upperLeftTileToBeDrawn = ScreenToTileCoords(e.ClipRectangle.Left, e.ClipRectangle.Top);
Point lowerRightTileToBeDrawn = ScreenToTileCoords(e.ClipRectangle.Right - 1, e.ClipRectangle.Bottom- 1);
Also, make sure your control is double-buffered, to avoid tearing. This is much simpler than #Steve B's link in the comments above states; assuming this is a UserControl, simply set
this.DoubleBuffered = true;
Well, what about this:
Do not clear the whole board but only those parts that need to be cleared.
Alternative:
Update to WPF - it moves drawing to the graphics card - and just move pieces around, in a smart way (i.e. have a control / object for every piece).
I was testing a DX scrolling banner that I've recently completed. The problem is that it's consuming very large amounts of memory.
The string to be scrolled is created using an array of characters and their sizes (using DX measure string) created from the source string (rss feed) and stored in an array. The rss feed, in this case, was about 500 characters long.
To create the scrolled string, I simply add/remove characters as the come into/outof view (using panel width & character sizes to determine the time to add/remove characters from the string) on the display panel. It works very well but is using 200M of memory. The string is never more that about 80 characters long (I've made sure of this by using an alert if it exceeds 80 chars). The amount of characters in the string is dependant, of course, on the size of the font.
If I comment out the DrawText command, the app uses little memory (proving that it's not some other part of the code). If I just scroll the entire string (500 chars) the memory used is only 32M. However, I'm now using lots of Proc scrolling such a large string.
I've noticed that when you draw a static (not scrolling) large string and then follow it with, say, a single character string, DrawText does not free the memory used to draw the large string. I'm using theString.Remove & theString.Insert to create the string to be scrolled. The memory seems to crawls up as each character is Added/Subtracted and once the entire RSS feed string has been scrolled, the memory usage stays static - at 200M - from then on in.
What's going on? Any help very much appreciated... it's driving me nuts! I could just split the feed into strings but it makes more sense to do it character by character.
private void sync()
{
if (device.RasterStatus.ScanLine)
{
UpdateDisp();
if (ArStringSegments.Count != 0)
{
for (int i = 0; i != Speed; i++)
{
# region Add a character to the displayed string
if (FirstCharLength == 0 && AddChrIndex != ArStringSegments.Count)
{
StringProps StringProps = (StringProps)ArStringSegments[AddChrIndex];
if (ScrollDirection == Direction.ToLeft)
{
theString = theString.Insert(theString.Length, StringProps.String);
}
AddChrIndex++;
}
# endregion Add a character to the string
# region remove a character from the string as it goes beyond the veiwable area
if (RemoveChrIndex != ArStringSegments.Count)
{
if (ScrollDirection == Direction.ToLeft)
{
if(ScrollInc == 0 - LargestChar)
{
StringProps RemoveString = (StringProps)ArStringSegments[RemoveChrIndex];
theString = theString.Remove(0, 1);
ScrollInc += RemoveString.Size1;
RemoveChrIndex++;
}
}
}
# endregion remove a character from the string as it goes beyond the veiwable area
# region Increment/decrement position
if (ScrollDirection == Direction.ToLeft)
{
ScrollInc--;
FirstCharLength--;
}
# endregion Increment/decrement position
# region Entire string has gone out of viewable area, scroll out by an amount and then start again.
if ((ScrollInc == 0 - (ScrollOutLength + LargestChar ) && ScrollDirection == Direction.ToLeft) ||
(ScrollInc == PanWidth + (ScrollOutLength + LargestChar) && ScrollDirection == Direction.ToRight))
{
theString = "";
AddChrIndex = 0;
RemoveChrIndex = 0;
FirstCharLength = 0;
if (ScrollDirection == Direction.ToLeft)
{
ScrollInc = this.PanWidth;
}
else
{
ScrollInc = 0;
RightBoundary = 0;
}
}
# endregion entire string has gone out of viewable area, scroll out by an amount and then start again.
}
}
}
ScanCount = device.RasterStatus.ScanLine;
}
private void UpdateDisp()
{
try
{
if (device.CheckCooperativeLevel(out DeviceStatus))
{
if (UpdateDisplayEnabled == true)
{
device.Clear(ClearFlags.Target, Color.Black, 1.0f, 0);
device.BeginScene();
// background image Texture
BackSprite = new Sprite(device);
BackSprite.Begin(SpriteFlags.AlphaBlend);
BackSprite.Draw2D(PanelTexture, new Point(0, 0), 0.0F, new Point(0, 0), Color.White);
BackSprite.End();
BackSprite.Dispose();
// draw the text
Draw2DText(FeedText[i], ScrollInc, 0, FontColor);
font.DrawText(null, theString, new Rectangle(ScrollInc, 0,
device.EndScene();
if (device.CheckCooperativeLevel(out DeviceStatus)) device.Present((IntPtr)DxRenderPanel);
}
}
}
We can't see your code, and so all I can give you is conjecture. With that in mind:
.Net uses garbage collection to manage memory. To make things more efficient, the garbage collector might decide it doesn't need to free memory until there's pressure from somewhere to do so. If you have a machine with several gigabytes of free ram, than there's no pressure to release it and this might be just fine.
That said, there are some things you could be doing wrong to prevent the garbage collector from release ram that it might otherwise be perfectly happy to return to your system.
Are you holding on to reference to your string images somewhere long after you need to?
Are you creating the images much faster than is needed?
If the scrolling string is marquee that will cycle, could you instead cache each image in the cycle rather than draw a new one?
Could you build one image (or two for the wrap-around) and only show the relevant portion by moving your control and hiding the edges behind something?
Are you creating a new string object for every DrawText() call, rather than re-using them?
Are you creating a lot of new font objects (maybe one per character) that you don't really need?
Most of all, are you drawing into a brand new bitmap every time, or could you clear an existing bitmap and draw the new string over that?