Graphics are too slow in my project - c#

I am developing zoo simulator project. It contains three thing types to draw: a map, animal environments and the animals themselves. The map is too big to fit on screen, player needs to move screen to see other parts of it. I am using a timer to draw. On its tick, it calls Invalidate() for the form being drawing on. In ZooForm_Paint method, I first draw every thing in the map on mapBuffer Bitmap. Since mapBuffer is too big to fit on screen, I draw (on screen) the part of mapBuffer the player is where.
Unfortunately, it seems that drawing everything in the map (although it may not be viewed) on mapBuffer slows the game. Can I draw my evironments and animals without need to draw entire map first?
How?
My code:
public void DrawGame(Graphics g, ref Point locationOnMap)
{
this.drawBufferMap();
this.drawMapLocation(g, ref locationOnMap);
}
private void drawBufferMap()
{
Bitmap buffer = new Bitmap(this.map.Size.Width, this.map.Size.Height);
using (Graphics graphics = Graphics.FromImage(buffer))
{
graphics.DrawImageUnscaled(this.map.Picture, new Point()); // draw entire map
foreach (var item in this.zoo.Environments) // draw all env.
{
graphics.DrawImageUnscaled(item.Picture, item.Bounds.Location);
}
foreach (var item in this.zoo.ILocatables) // draw all ILocatables
{
graphics.DrawImageUnscaled(item.Picture, item.Location);
}
}
if (this.mapBuffer != null)
{
this.mapBuffer.Dispose();
}
this.mapBuffer = buffer;
}
private void drawMapLocation(Graphics g, ref Point location)
{
g.DrawImage(this.mapBuffer, new Rectangle(0, 0, viewSize.Width, viewSize.Height),
new Rectangle(location.X, location.Y, viewSize.Width, viewSize.Height), GraphicsUnit.Pixel);
}

I don't think you are going to get any easy solutions. I can offer a few tips and opinions:
You seem to be creating a new BitMap every time you paint the screen. This is definitely not a good idea, as large bitmaps are absolutely huge in terms of memory. What you probably want to do is create one when your game loads, and then simply clear it and repaint it at every frame. I think this is probably one of the bigger performance issues you have.
There are a number of optimisations you could make afterwards. E.g. you are "rendering" the image that you will end up painting to the screen on the user interface thread. If the rendering process takes long, this will be noticeable. Typically this work happens on a background thread, and then the UI thread just checks if it can repaint using the new image. (I am simplifying things greatly here).
For graphics intensive applications, WinForms is not a particularly good environment, as others have pointed out. You will not get any hardware acceleration at all. Moving to XNA is one option, but if your application is also quite rich in terms of standard WinForms screens and controls, this is probably not an easy option. Another suggested alternative might be WPF, where you might be able to get away with using transformations to move things around, which are hardware accelerated, and are not too dissimilar to a WinForms application (well, you don't need to implement your own buttons, etc).
Hope this helps a bit.

As Daniel pointed out: creating a new bitmap each time you need to draw your map will decrease performance. Reuse the same bitmap over and over instead.
Creating a bitmap larger that you need is also very bad for performance. If you need it to scroll around, then it's fine. But if you paint a new image each time anyway, then you should just create it exactly the same size you need. Then you can call Graphics.TranslateTransform to compensate for the new coordinates so you can leave your existion code unchanged.
This will make it possible for GDI+ to clip your graphics and simply just don't draw things outside your map bitmap - which will speed things up.

Related

Copy OpenGL back buffer directly onto GDI DC pixel data

I'm writting a GUI wich uses OpenGL via the OpenTK and the GLControl on C# and i'm trying to use dirty rectangles for drawing only the controls that need to be drawed. Obviusly it's not wise to redraw an entire maximized form just for refreshing a mouse-hover button.
My first attempt was to use glScissors but this doesn't limit the SwapBuffers, wich in my platform, I suspect (because of the performance almost entirely dependent on the window size) doesn't 'swap' but do a full copy of the back buffer onto the front buffer.
The second attempt was the glAddSwapHintRectWIN wich in theory would limit the swapped (in this case copied) area of the SwapBuffers, but this is only a hint and it doesn't do anything at all.
The third attempt was the glDrawBuffer to copy a part of the back buffer onto the frame buffer, for some unknown reason, even when i copy only a part of the buffer, the performance still decreases the same way before when the window size increase.
It seams that a full-area refresh it's still hapening no matter what i do.
So i'm trying to use the glReadPixels () and somehow get a pointer to draw directly onto a hDC pixel data getted from the CreateGraphics() of the control. Is this possible?
EDIT:
I think something is wrong with the GLControl, why the performance of this code depends on the screen size, i'm not doing any swapbuffers or clearing, just drawing a constant-size triangle on the front buffer:A driver problem, maybe?
GL.DrawBuffer(DrawBufferMode.Front);
Vector4 Color;
Color = new Vector4((float)R.NextDouble(), 0, 0, 0.3F);
GL.Begin(BeginMode.Triangles);
GL.Color4(Color.X, Color.Y, Color.Z, Color.W);
GL.Vertex3(50, 50, 0);
GL.Vertex3(150F, 50F, 0F);
GL.Vertex3(50F, 150F, 0F);
GL.End();
GL.Finish();
EDIT 2
This solutions are not viable:
Drawing onto a texture and using glGetTexImage for drawing onto a GDI bitmap and then drawing that bitmap onto the window hDC
Reading buffer pixels from the buffer using glReadPixels onto a GDI bitmap and then drawing that bitmap onto the window hDC.
Splitting the window onto a grid of viewports and updating only the cells that contains the dirty rectangle
First of all, what platform (GPU and OS) are you using? What kind of performance are we talking about?
Keep in mind that there are several limitations when trying to combine GDI and OpenGL on the same hDC. Indeed, in most cases this will turn off hardware acceleration and give you OpenGL 1.1 through Microsoft's software renderer.
Hardware accelerated OpenGL is optimized for redrawing the entire window every frame. SwapBuffers() invalidates the contents of the backbuffer, which makes dirty rectangles impossible to implement when double buffering on the default framebuffer.
There are two solutions:
do not call SwapBuffers(). Set GL.DrawBuffer(DrawBufferMode.Front) and use single-buffering to update the rectangles that are dirty. This has severe drawbacks, including turning off desktop composition on Windows.
do not render directly to the default framebuffer. Instead, allocate and render into a framebuffer object. This way, you can update only the regions of the FBO that have been modified. (You will still need to copy the FBO to screen every frame, so it may or may not be a performance win depending on your GUI complexity.)
Edit:
40-60ms for a single triangle indicates that you are not getting any hardware acceleration. Check GL.GetString(StringName.Renderer) - does it give the name of your GPU or does it return "Microsoft GDI renderer"?
If it is the latter, then you must install OpenGL drivers from the website of your GPU vendor. Do that and the performance problem will disappear.
After several test with OpenTK, it appears that in single or double buffered mode, the slowdown observed with control size increasing still remains, even with constant size scissor enabled. Even the use or not of GL.Clear() doesn't impact slowdown.
(Note that only height changes has significant impact.)
Testing with ansi c example, I had the same results.
Making the same couple of tests under linux gave the same results too.
Under linux I noticed that frame rate changes when I move from one display to the other. Even with vsync disabled.
Next step would be to check if directX has the same behaviour. If yes, than the limitation is located on the bus between display and graphic card.
EDIT: conclusion:
This behaviour is leading you to false impression. Consider only building your interface on a FBO with dirty rect mechanisms and render it on a quad (made of tri's is better) and swap as usual without thinking that you can improve swapping for a given window size by clipping some operations.

Drawing a single large image with the graphics card in a custom control

Simply put, I'm trying to draw an image (2560x2048) that is supposed to be zoomed / draged and such but the performance is very bad, because it flickers everytime I move it. I use a custom control to be able to drag the image to a new position and zoom in and out, which means it have to be flexible and fast.
So, what is the easiest and best way to just draw a single image with the graphics card? Without having to initialize a thousand directX objects just for one simple purpose.
Overall, the application is a tool - so not a game. But this particular large image is supposed to be drawn effectively.
Double buffering is your friend http://msdn.microsoft.com/en-us/library/3t7htc9c.aspx
Well I am Borland C++ friendly so i would use simple Canvas (simple Windows GDI interface).
No need for GL,GLSL or DX
you need 2 bitmaps.
one as source image (that is your 2560x2048) next is back-buffer of the screen (client size of your view area). Both has to be DIB. I prefer pf32bit pixel format (32 bit int as a color so I can use int *p ...)
need to write render function (or use GDIs strech draw or CopyRect)
do not use Set or Put or Pixels they are slow (always checking size and color format and many other stuff for each pixel). Instead use bitmaps scan lines (in VCL bmp->ScanLine[y]).
This usually speeds up things about ~1000 times if done properly
put it all together
clear back-buffer with background color. Use your render function to copy viewed area to back-buffer (no need to render whole image). When all rendering is done copy back-buffer to the screen.
when all works you can further speed up things
use array of scan lines pointers instead of calling them in rendering. Addresses of scan lines are changed only after resizing of bitmap so on resize delete all array of pointers and create and fill new one (int *pyx[ys];)
This kind of bitmap rendering is fast enough for simple software 3D rendering so for your purpose must be sufficient with high enough framerate (estimate well over 70fps on average desktop machine)
some code for Borland C++ VCL (just so you know what to look for in your programing languge)
// init
int xs=0,ys=0,*pyx=NULL;
Graphics::TBitmap *bmp=new Graphics::TBitmap;
bmp->HandleType=bmDIB;
bmp->PixelFormat=pf32bit;
// exit
if (bmp) delete bmp; bmp=NULL;
if (pyx) delete pyx; pyx=NULL;
// resize(_xs,_ys)
if (pyx) delete pyx; pyx=NULL;
bmp->Width=_xs;
bmp->Height=_xs;
xs=bmp->Width;
ys=bmp->Height;
pyx=new int*[ys];
if (pyx==NULL) return; // not enough memory
for (int y=0;y<ys;y++) pyx[y]=(int*)bmp->ScanLine[y];
// now pyx[y][x]=0; means setpixel(x,y)=color(0) without any slowing down checks
// now c=pyx[y][x]; means colro(c)=getpixel(x,y) without any slowing down checks
// but beware of accessing x,y, outside <0;xs),<0;ys) !!!

Using Bitmap in Bouncing Balls

I have a WinForm application 'Bouncing Balls' , and I need to paint the balls
on a bitmap and present the bitmap on this form.
I have a plusButton that adds new ball, and i'm saving each new ball in a list.
Now, the Form_Paint method is telling to each ball to draw himself, it works fine
until there are a lot of balls and the all application become very slow..
Here is my Code:
The paint method of the form code:
private void Form1_Paint(object sender, PaintEventArgs e)
{
ballsArray.drawImage(bmp,e, ClientRectangle);
}
NOTE: ballsArray is from type AllBalls, this is a class that wraps the ball methods, inside his c'tor i'm creating a list that keeps each ball. the bmp, is created when the form is loading - on Form_Load() method.
The drawImage of ballsArray code:
public void drawImage(Bitmap bmp,PaintEventArgs e, Rectangle r)
{
foreach (Ball b in allBalls)
{
b.drawImage(bmp,e, r);
}
}
The drawImage of Ball code:
public void drawImage(Bitmap bmp, PaintEventArgs e, Rectangle r)
{
using (Graphics g = Graphics.FromImage(bmp))
{
e.Graphics.FillEllipse(brush, ballLocation);
g.DrawImage(bmp, 0, 0);
}
}
NOTE: ballLocation is a rectangle that represent the location of the ball in each
step of movement..
So what I'm doing wrong? What causing the application to be slowly?
I have a constraint to draw everything on the bitmap and present it on the form.
I'm also passing the bitmap that I create when the form is loading, because I need to draw each on it.
Some basic techniques to make this fast:
Don't double-buffer yourself and especially don't double-buffer twice. The double-buffering you get by setting the form's DoubleBuffer property to true is superior to most any double-buffering you'd do yourself. The buffer is highly optimized to work efficiently with your video adapter's settings. So completely drop your bmp variable and draw to the e.Graphics you got from the Paint event handler argument.
You are not using the passed r argument. Possibly intended to support clipping invisible balls. The one you want to pass is e.ClipRectangle, you can skip painting balls that are completely outside of this rectangle. While that's an optimization, it isn't one that's commonly useful when you use the Aero theme and you do get inconsistent redraw rates so you might want to skip that one.
It isn't very clear why you use both Graphics.FillEllipse and Graphics.DrawImage when you draw the ball. The image ought to overlap the circle so just remove FillEllipse.
Pay a lot of attention to the Bitmap object that stores the ball graphic. First thing you want to make sure is that it is drawn with the exact size of the image so it doesn't have to be rescaled. Rescaling is very expensive. While you don't have any rescaling in your DrawImage() call, you will still get it if the resolution of the bitmap is not the same as the resolution of your video adapter. The next step will solve that
The pixel format of the ball bitmap is very important. You want one that permits copying the bitmap straight to video memory without any format conversion. On any modern machine, that format is PixelFormat.Format32bppPArgb. The difference is enormous, it draws ten times faster than any of the other ones. You won't get this format from an image resource you added, you'll have to create that bitmap when your program starts up. Check this answer for the required code.
You ought to be able to render at least 15 times faster when you follow these guidelines. If that's still enough then you do need to turn to DirectX, it has the unbeatable advantage of being able to store the ball graphic in video memory so you don't get the expensive blt from main memory to video memory.
DrawImage on Paint (or for that matter on MouseMove) is very bad design.
Graphics.DrawImage is expensive operation, and to the screen it is extra expensive.
To improve your user experience (slowness), You should paint on MouseDown/MouseUp events.
In addition, First draw to MemoryBuffer in your drawImage method and after preparing the final image, draw it once on the UI. This technique is known as double buffering.
Don't Flicker! Double Buffer! - CodeProject
In addition you can also look at BitBlit Native API for fast color/image transfer to screen.
A minimalistic c# example is here
Enable double-buffering on your form (DoubleBuffered = true).

C# combine bitmaps fast for UI

Basically I'm making an animated UI from scratch. Everything on the UI is derived from a ScreenThing base class which can store a bitmap used to draw that thing, as well as its position. When I want to draw the UI I create a new blank Bitmap of the right size, create a Graphics object from that bitmap, then pass that Graphics object to all of the ScreenThings Draw() method to draw themselves. When that's done, I print the bitmap to the screen with a PictureBox control.
This all works great, but it's too slow. I want this to happen at least 30 times per second so the animation is smooth, but it's taking more than 33 milliseconds for all of this to happen. I've seen that the reason for this slowness is the locking and unlocking of the bitmap memory. I've tried some code I found online that unlocks the bitmaps and makes a new GetPixel and SetPixel function and tried combining the images pixel by pixel like below, but this took even longer. (code below was just a test, it doesn't place the image in the right place)
for (int i = 0; i < images.Count; i++)
{
FastBitmap foreground = new FastBitmap(images[i]);
foreground.LockImage();
for (int x = 0; x < images[0].Width; x++)
{
for (int y = 0; y < images[0].Height; y++)
{
output.SetPixel(x, y, foreground.GetPixel(x, y));
}
}
foreground.UnlockImage();
}
So what's the best way to do this? Is it possible to use C# to draw a pretty big image (like 1024x768) in real time like this? If all else fails, I guess I could figure out a way to only draw the parts that have changed, but I'd love to avoid doing that if a brute force redraw of everything is possible. Thanks a lot!
You should never use bitmaps to draw real-time animations. Just create a CustomControl, and override its OnPaint method like this
protected override void OnPaint(PaintEventArgs pe)
{
}
there you can use pe.Graphics to do any drawing you want and no copying to screen is required.
I've used it and if you're drawing routines are reasonably fast, that will be smooth.
Also, remember to call SetStyle(ControlStyles.OptimizedDoubleBuffer, true); in the control constructor to get double buffering
The pixel-by-pixel approach is the worst you can do. Instead you should profile you application. This is simple: Run it at full workload (100% cpu) and pause the debugger 10 times. Look at where it stops most, that is your hotspot that you have to optimize.

equivalent CreateGraphics in wpf

So, I've used winForms .CreateGraphics to draw a variety of different things, from lines to boxes to images. It was very snappy and responsive.
I am trying to learn WPF in C#
I found that WPF allows me to "add" rectangle objects to a canvas which will display them properly. HOWEVER, I am drawing hundreds of thousands of rectangles at times, and the draw rate can become exceedingly slow, and the UI becomes less snappy when I move even 1 of the rectangles.
Painting directly onto an element in winForms was not very fast, but it was consistent regardless of how much I painted.
Is there a similar solution to doing this in WPF?
I tried adding a linq to System.Drawing, which gave me a Graphics object, but none of the wpf elements i tried have the .CreateGraphics() method.
WPF uses a different model for graphics manipulation than WinForms.
With WinForms, you are able to directly edit the pixels on the screen. The concept of your rectangle is lost after the pixels are drawn. Drawing pixels is a very fast operation.
With WPF, you are not controlling the pixels on the screen. DirectDraw is. DirectDraw is a vector-based compositing engine. You do not draw pixels. You define vector shapes (or visuals). The concept of a shape, or a rectangle, is RETAINED, even after the image is rendered to the screen. When you add a new rectangle which overlaps the others, ALL OTHER RECTANGLES NEED TO BE REDRAWN. This is likely where your performance is slowing down. This does not happen when using WinForms.
You can improve the performance of WPF a bit by overriding OnRender. You can cut out the overhead of the Rectangle object and directly provide the visuals. However, you are still not drawing pixels to the screen. You are defining shapes that DirectDraw uses to render the image. In this regard, the OnRender name may be a bit misleading.
I am sure you can find plenty of tricks to improve performance of your application in WPF. There are ways to still paint pixels - but that is kinda defeating the point of WPF.
What are you doing that requires thousands of rectangles?
You would need to create a control that overrides OnRender and do your drawing in there. There isn't a way for you to draw onto another control, but a control can draw itself.
Also, keep in mind that WPF uses retained graphics, so if you change something you need to invalidate the visual as needed.
EDIT:
Something like:
public class MyControl : Control {
public MyControl() {
this.Rects = new ObservableCollection<Rect>();
// TODO: attach to CollectionChanged to know when to invalidate visual
}
public ObservableCollection<Rect> Rects { get; private set; }
protected override void OnRender(DrawingContext dc) {
SolidColorBrush mySolidColorBrush = new SolidColorBrush();
mySolidColorBrush.Color = Colors.LimeGreen;
Pen myPen = new Pen(Brushes.Blue, 10);
foreach (Rect rect in this.Rects)
dc.DrawRectangle(mySolidColorBrush, myPen, rect);
}
}
As was said, WPF uses a retained graphics methodology so your actually creating 100,000 Rectangle objects in memory and then drawing all of them. The slowdowns are probably due to garbage collection and general memory issues.
Aside from override the OnRender method, here's a couple of things you could look into though.
Drawing the rectangles to an image in a background thread using the GDI methods your familiar and then write the result to a WPF WriteableBitmap
Use the D3DImage and take advantage of hardware acceleration. This requires you to know the DirectX (or Direct2D) libraries. If your interested in this approach, I'd suggest looking into SlimDx.
The problem is most likeley not that WPF can't render 1000s of graphic objects, but that your creating and adding items too far up the WPF object hierachy. It does after all use the GPU for all the graphical grunt work.
You should add objects as close to the "Visual" class as possible, as soon as you start adding objects based on the latter "UIElement" you are asking WPF to track user clicks, hovers and so on for each object, not just draw it.

Categories