I've got an example app which draws Rectangles, Lines etc. using the DrawContext instance of the OnRender override of a Control class. This control is repainted every 10 milliseconds by calling InvalidateVisual (I can post the source to anybody who's interested). I calculate the Frames per second (FPS) by measuring the time between each call of OnRender.
However, this figure for FPS is incorrect. Just by looking at the app I can see that the figure given for FPS is higher than the number of times per second that the app repaints itself. This is because "When you use a DrawingContext object's draw commands, you are actually storing a set of rendering instructions (although the exact storage mechanism depends on the type of object that supplies the DrawingContext) that will later be used by the graphics system; you are not drawing to the screen in real-time."
So what I would like to know is if there is any event I can subscribe to, or any other way, to ascertain how many times per second my WPF app/control is generating a new bitmap and drawing it to the screen? Is there any bitmap buffer held by the "graphics system" which we can access?
Many thanks!
What you need to use is the CompositionTarget.Rendering event that is called every frame.
Related
I'm using a SwapChainPanel to render a control. The render method attaches to the CompositionTarget.Rendering event.
Also, RenderTarget.CreateCompatibleTarget is called to create an offscreen target. The compatibleTarget.Bitmap property is called to create a cached bitmap that can be blitted onscreen.
During each frame:
BeginDrawing() is called on the onscreen target.
If the scene has been invalidated by program logic, it is redrawn to the offscreen target.
The onscreen target is cleared using the background color. Without this, successive frames are somehow blended into each other.
The offscreen bitmap (cached above) is drawn onto the onscreen target using onscreenTarget.DrawBitmap(cachedBitmap), with opacity set to 1.
onScreenTarget.Flush() is called to flush the contents.
EndDrawing() is called on the onscreen target.
I find that this gives a very low frame rate.
Comparison with WindowRenderTarget
For comparison, I tested the exact same scene code in a WinForms app using a WindowRenderTarget. (SharpDX makes this possible since it works on UWP as well as desktop.) This gives a much higher frame rate, and zero steady-state CPU consumption.
Questions:
Why does SwapChainPanel produce such a low frame rate compared to WindowRenderTarget?
Why is it necessary to clear the onscreen target each frame before drawing the bitmap in step 4 even when the opacity is 1?
Can I avoid steps 1-6 if nothing has changed? This consumes around 7% CPU.
I don't think there should be too much performance difference between SwapChainPanel & WindowRenderTarget, because both of them are almost directly based on DirectX components. You could compare their settings to investigate the performance differences: Is the SwapChainPanel related with D2D device? Any differences among the configuration of devicecontext, swapchain description, and WindowRenderTarget's renderTargetProperty; Are there any difference between the bitmap processing methods?
THe opacity is set for the image you are drawing. However, the clear() if for the overall rendering view.
When you mean "if nothing changed", I supposed you mean you have nothing to draw. Then of course you can do nothing. Otherwise, the step from 1 to 6 are necessary.
Besides, you can find a good documented SwapChainPanel example here. Some settings in this example are for performance improving.
My question might seem silly to you, but I realized that moving my applications form makes the code inside it run slower. E.g. when I load a bitmap image and apply some image editing algorithms on it, it takes about 22 secs for the whole process to finish. But if I move the form during execution, it adds some 3-4 extra seconds to the elapsed time. I was able to spot the delay using a Stopwatch. So how can I get around this behaviour, if possible at all?
This is just an hypothesis that requires your investigation, as you didn't post any code and thus it is impossible to really know what is going on.
Most probably you move the boundaries of the image outside the screen. When you move in again, the windowing engine will do some draw calls on those rectangles to be redrawn. The same happens on resize when you enlarge but not when you shrink the window.
If this is the case, then you will not experience any extra draw calls as long as you don't cover/uncover areas of the image.
So this is not an answer but in your place I would override the Paint() method and log how many excess calls are made. Based on this, I'd search for a solution, such as suppress those calls like this:
public override void Paint()
{
if (algorithmRunning)
{
return; // suppress any further computations
}
base.Paint(); // do actual redraws
}
This code is just an example, you'll have to fix it according to the MSDN documentation.
What you should NOT do is just hook into the OnPaint() event, because then you'll still have the actual Paint() method called.
I am doing some work for which I need to develop a control, it should be a simple graph that shows several points and two edges.
My problem is that I need to show up to 16k points, with an update rate of 30 Hz. Has anyone done something similar?, and has any advice?.
For example whether to inherit from FrameworkElement or Control (ItemsControl in this case). If the control inherits from FrameworkElememt it may have a better performance drawing the points in the OnRender method but I would miss the Templating feature that comes from inheriting from Control.
Or does there exist another control that can do this out there?
Thanks in advance for your time.
I ended up using InteropBitmap, it is the fatest bitmap rendering class from WPF.
It allows you to map the image that you want to paint (in memory) and then reder it as a Image. This was perfect as i needed to plot points on the screen.
I got great performance (almost 50Hz for 20k points), i also use PLINQ to update the points in memory.
check this article for more details...
Try and read about ZoomableCanvas. I believe it can solve your problem. You can render all the points as small rectangles/ellipses inside the ZoomableCanvas.
I need to load 101 bitmaps from the filesystem (they can't be built into the app as a resource as they will be changed for each run of the program) into a windows form application picturebox sequentially based on short duration timer events (500ms give or take) .
Essentially it should work like a slow animation but it is critical that every image be shown.
I've roughed out a simple application in C# to do this however it seems that the image loading and displaying is taking longer than the 500ms so some images are never displayed.
Is there some way I can avoid this? Preloading or creating 101 pictureboxes and showing and hiding?
Anybody have any suggestions?
What's taking the longest? Image loading or displaying? Are you loading each image when it needs displaying? A look at your code would be really valuable.
If you are certain that it will always be exactly 101 images, load them all into an array of System.Drawing.Bitmap first then have an iterator variable that gets incremented on each call of the Tick event of a Timer. Have this Tick event load the image from the array into the PictureBox using PictureBox.Image = myBitmapArray[iterator] If you increment the iterator using ++i%=101; you won't get an OutOfBounds error and the animation will loop.
Populate an array of Bitmap objects before starting the animation.
A couple of options... pick one or combine them:
(1) Use a lock and a counter to guarantee that when the event fires it's loading the next image in line.
(2) Disable the timer in the Tick event, then re-enable it after you've loaded the image. The result is that the images arrive 500ms after the last one is drawn, so if a picture takes one second to load, the images are drawn at t=1000ms, t=1500ms, t=3000ms, etc. All the images are drawn, and 500ms is guaranteed to pass between pictures, but the animation might appear slow.
(3) Do the above, but track the time the event starts, and after the image is drawn, set the next timer tick to be 500 - (Now - eventStart)... so that if the image takes 250ms to draw, the next timer tick will fire in 250ms. If Now - eventStart < 0, the next timer tick should fire immediately. The animation will take the minimum amount of time possible, but images could potentially flash by, appearing only for a few milliseconds.
(4) Use PictureBox.LoadAsync() to give you some multi-threading... the next event can be loading the image while the previous event is drawing. But you'll need a Mutex that you release in the LoadCompleted event that you wait on before calling LoadAsync(), if you need to guarantee the images are all drawn.
(5) I'm not sure if the drawing of the picture falls under the Layout category, but you can try calling SuspendLayout() and ResumeLayout() before/after loading the image
(6) Use an array of images, lock a counter, and use the .Image property of the PictureBox to let you pre-load the images. I believe this was suggested in another answer, also
HTH,
James
I don't have a concrete answer for you, but I would approach this by first determining if it's image loading, image display, or both actions that are taking too much time.
I would imagine that image loading is going to be relatively quick, even with pretty large image sizes, provided that the images are local and you have relatively decent hardware. My first attempt would involve loading all images from a thread sequentially so that you application doesn't have to wait for image display to complete before the next image loads.
If image display is taking a long time (and even on my really powerful workstation it's not all that fast for large images), then is it possible for you to scale the images before the application displays everything? Does your application need to deal with full resolution images? With the megapixel cameras these days, I can't image that you'd want to have all of the data present in the image files anyway, since the sizes can easily exceed monitor resolutions by a factor of 6.
Another thing that may be a concern is the size of the images. If the are all 18 Megapixel Images, I can see it taking either a lot of time or a lot of room.
You may want to resize them to the size of your display area when you initially load them up so that you aren't using 2 gigs of ram or waiting to read off the disk depending on how you implement it.
I have a C# .NET application with which I've created a custom image display control. Each image display represents its own display context and draws the image using glDrawPixels (Yes I know it would be better to use textures, I plan to in the futures but this app is already too far along and my time is limited).
I am now trying to have both images pan simultaneously. That is, when one image is moved down ten pixels, the second image moves down ten pixels. Like so:
imageOne.YPan -= 10;
imageTwo.YPan -= 10;
imageOne.Invalidate(); //This forces a redraw.
imageTwo.Invalidate(); //This forces a redraw.
Alright so here is the problem I am having. Only one of the images displays is redrawing. If I place a pause in between the two Invalidate calls and make the pause duration at least 110 milliseconds both will redraw, but not simultaneously. So it looks as if the second image is always trying to catch up to the first. Plus, a 110 millisecond pause slows down the motion too much.
I have tried placing the updating and invalidating of each image in its own thread but this did not help.
At the beginning of drawing I make the appropriate context is current, and at the end I am calling swapbuffers(). I tried adding a glFinish to the end of the draw function, but there was no change.
Could it be that its the graphics card that is the problem? I am stuck using an integrated gpu that only has openGL 1.4.
Hopefully, I have provided enough detail that the answer to my problem can be found.
Its difficult telling what's wrong with what you do since you give so little detail. Here are some pointers which may help.
- before doing something in a context, make sure you make it the current one. If you want to pan two contexts, make the first one current, pan it and then make the second one current and pan it. These is no real reason why this should not work.
- If it looks like there is a timing problem, adding glFinish() at strategic places may help weed the problem out
- As should always be done, on occasions call glError() and see that everything went well.
- I'm not sure how this is done in the framework you're talking about but you should make sure that both contexts get a swapBuffers() call for every frame.
Invalidate doesn't force an immediate redraw. It marks the window invalid, and when the message queue runs out of other messages, a paint message will be created and processed. But that won't happen until you finish processing the current message and return to the main message loop, and it may be delayed even more than that.
Generally OpenGL animation is an exception to the rule of doing all drawing inside Control.OnPaint (or in a handler for the Control.Paint event).