As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 11 years ago.
Say I am making a 2D Tile Based game in C# using System.Drawing for graphics.
OOP Layer for GDI?
The System.Drawing namespace provides access to GDI+ basic graphics functionality. ~ MSDN
Back when I was starting out Graphics & Game programming in C, a lot of books such as this one taught me that a software-rendered API like GDI was not efficient enough for Double-Buffered 2D games running at a decent FPS. So does Microsoft .NET's System.Drawing implementation fall into the same category? Does it use GDI & GDI+ under the hood, or can it use a different, faster backend?
How Much FPS Can I Hope For?
Even if System.Drawing can only use GDI, is it impossible to make a game run at a decent Frame rate, say 50-60 frames per second, with GDI?
It's not really a question of it being fast enough as it is it being suitable for the task you want to perform. In all honesty, it's quite easy to implement basic functionality to do the drawing you require using for instance XNA, and you get so much for free. There is simply no reason to go into System.Drawing for making games.
The book you linked in quite ancient, while it contains a few algorithms that never goes out of style most of it is really outdated.
As to how much FPS you can get from System.Drawing, you can probably get enough FPS from it from a simple tile based game. It's going to work if you do it that way, but it will hinder you from making real progress later on - especially as the threshold for making games using XNA is so low these days.
It is quite unguessable what kind of FPS you'll get, it entirely depends on how intricate your scenes are. The biggest restriction in GDI/+ rendering is that bitmaps are stored in main memory, not video adapter memory. So most anything you draw has to be copied from RAM to the video adapter's frame buffer. Sure, that's relatively slow but unavoidable as long as you don't adopt an API that can manage memory on the video adapter. There are few reasons left to avoid this kind of API, there isn't any hardware left that doesn't support DirectX.
The equivalent of page flipping is easy to get, just set the form's or control's DoubleBuffered property to true. Anything you draw goes into a back-buffer first, that buffer is blitted to the video adapter after the Paint event completes. The only thing you won't get is synchronization with the adapter's vertical refresh interval so tearing is pretty unavoidable. How noticeable that will be depends a great deal on how quickly you move objects in the scene.
The only other detail is the pixel format of any bitmaps you draw. Sprites, textures, that sort of thing. The format you pick is very important. On current hardware, Format32bppPArgb is ten times faster than any of the other ones. If you do any rescaling on the bitmaps then the Graphics.InterpolationMode property is important for speed. The good ones are expensive. You can rescale them in a worker thread as long as you make sure that only one thread ever accesses the bitmap at the same time.
But be sure to have a look at DirectX (SlimDX for example) and XNA. Hard to beat for convenience, just a lot less work to do.
Related
Guten Tag zusammen! (German: Good Day everyone!)
Please excuse my English, it is not my mother tongue. Since I have already found a lot of good answers to my questions here I would like now to ask you some.
In the last few days I have done a little research to the question "What is the best way to implement a game loop in Windows Forms?". And I have found a good explained solution form the SlimDX team, which it is based on the work of Tom Miller.
My Questions are:
First: We I use this solution for an game loop which is the best way to redraw the From after I have rendered a frame? A way I have often found is to call Invalidete(), but this does not look like a good idea to me. Wouldn't this add a message to the message queue and break the while-loop every frame?
Second: To my understanding this loop will consume an entire CPU-Thread (Core). Is there and good way to slow it down to a recommended frame rate that does not consume an entire CPU-Thread?
Third: Is GDI+ capable of render a simply 2D game?. When did Drawing becomes so complex that it is advisable to use a hardware accelerated drawing whit some DirectX or OpenGL wrappers?
A way I have often found is to call Invalidate(), but this does not look like a good idea to me. Wouldn't this add a message to the message queue and break the while-loop every frame?
Yes, that's a bad idea. You don't control the frequency at which the messages are delivered to your window, which means you shouldn't rely on it.
To my understanding this loop will consume an entire CPU-Thread (Core). Is there and good way to slow it down to a recommended frame rate that does not consume an entire CPU-Thread?
You can use the SpinWait structure (NOT Thread.SpinWait) to wait short amount of times. Base the waiting time on the duration of the curret frame and the desired framerate.
But usually the game loop taking an entire core isn't a problem. It may even be desired if you want to maximize your framerate.
Is GDI+ capable of render a simply 2D game?
Yes, although it'd be a really simple game. GDI is slow. I advise you to go with a hardware-accelerated solution right from the start, that way you won't have to rewrite everything if GDI proves to be a bottleneck.
I have a lighting system in my xna game that loops through each light, and adds these lights to a final light map, which contains all the lights.
The process to create these lights involves many functions that have to do with the graphics device, such as using effects / shaders and drawing to render targets, and using graphics.device.clear to clear the render target, etc
So my question is, would it be possible to multi thread each light? Or would this not be possible because there is only 1 graphics device, and only 1 thread can use it at a time? If it is possible, would it improve performance?
Basically no. The GraphicsDevice in XNA is single-threaded for rendering. You can send resources (textures, vertex buffers, etc) to the GPU from multiple threads. But you can only call Draw (and other rendering functions like Present) from your main thread.
I have heard of people having success doing rendering-type-things from multiple threads with the appropriate locking in place. But that seems like "bad voodoo". As the linked post says: "the XNA Framework documentation doesn’t make any promises here". Not to mention: even getting the locking right is tricky.
I'm not really sure about making multiple graphics devices - I've not tried it myself. I think that it is possible, but that you can't share resources between the devices - making it fairly useless. Probably not worth the effort.
As jalf mentioned in a comment on your question - once you get to the GPU everything is handled in parallel already. So this would only be useful if you are CPU limited due to hitting the batch limit (because almost everything that isn't your batches can be moved to another thread). And in that case there are many optimisations to consider first to reduce the number of batches - before trying a crazy scheme like this. (And you have measured your performance, right?)
It sounds like what you might be trying to do is render a fairly complicated scene to a render target in the background, and spreading the load across many frames. In that case - if performance requirements dictate it - you could perhaps render across multiple frames, on the main thread, scheduling it manually. Don't forget to set RenderTargetUsage.PreserveContents so it doesn't get cleared each time you put it on the graphics device.
We're going to do some experiments on perceptual thresholds and want to show an image for a very short time. I'm speaking about less then 10 ms (our screen supports 144 Hz, that is a new image every 6.94 ms).
But all our approaches until now failed. We tried it with C#: WinForms were much too slow, WPF was faster, but we were still able to see the image, and even showing a texture with XNA framework did not work for us.
Do you have any suggestions for us? We're allowed to use C++ too, but we prefer using C#, so if your suggestions works with C# we would really much appreciate.
I would highly recommend using the XNA Framework for this. Some might consider it overkill but the fact is it is first of all designed to handle high throughput of frames and secondly has a relatively small learning curve.
I'm not a game developer but started by reading an article, modifying a simple project and creating one from scratch.
This would probably be your best bet in terms of the technologies you mentioned in your question.
UPDATE: Regarding measuring the actual time an image is shown, XNA would again be your closest guess short of dealing with specialized hardware and resorting to low-level programming.
I've become rather familiar with the System.Drawing namespace in terms of knowing the basic steps of how to generate an image, draw on it, write text on it, etc. However, it's so darn slow for anything approaching print-quality. I have seen some suggestions using COM to talk to native Windows GDI to do this quicker but I was wondering if there were any optimizations I could make to allow for high speed, high quality image generation. I have tried playing with the anti-aliasing options and such immediately available to the Graphics, Bitmap and Image objects but are there any other techniques I can employ to do this high speed?
Writing this I just had the thought to use the task library in .Net 4 to do MORE work even though each generation task wouldn't be any quicker.
Anyway, thoughts and comments appreciated.
Thanks!
If you want raw speed, the best option is to use DirectX. The next best is probably to use GDI from C++ and provide a managed interface to call it. Then probably to p/invoke to GDI directly from C#, and last to use GDI+ in C#. But depending on what you're doing you may not see a huge difference. Multithreading may not help you if you're limited by the speed at which the graphics card can be driven by GDI+, but could be beneficial if you're processor bound while working out "what to draw". If you're printing many images in sequence, you may gain by running precalculation, rendering, and printing "phases" on separate threads.
However, there are a number of things you can do to optimise redraw speed, both optimisations and compromises, and these approaches will apply to any rendering system you choose. Indeed, most of these stem from the same principles used when optimising code.
How can you minimise the amount that you draw?
Firstly, eliminate unnecessary work. Think about each element you are drawing - is it really necessary? Often a simpler design can actually look better while saving a lot of rendering effort. Consider whether a grad fill can be replaced by a flat fill, or whether a rounded rectangle will look acceptable as a plain rectangle (and test whether doing this even provides any speed benefit on your hardware before throwing it away!)
Challenge your assumptions - e.g. the "high resolution" requirement - often if you're printing on something like a dye-sub printer (which is a process that introduces a bit of colour bleed) or a CMYK printer that uses any form of dithering to mix colours (which has a much lower practical resolution than the dot pitch the printer can resolve), a relatively low resolution anti-aliased image can often produce just as good a result as a super-high-res one. If you're outputting to a 2400dpi black and white printer, you may still find that 1200dpi or even 600dpi is acceptable (you get ever decreasing returns as you increase the resolution, and most people won't notice the difference between 600dpi and 2400dpi). Just print some typical examples out using different source resolutions to see what the results are like. If you can halve the resolution you could potentially render as much as 4x faster.
Generally try to avoid overdrawing the same area - If you want to draw a black frame around a region, you could draw a white rectangle inside a black rectangle, but this means filling all the pixels in the middle twice. You may improve the performance by drawing 4 black rectangles around the outside to draw the frame exactly. Conversely, if you have a lot of drawing primitives, can you reduce the number of primitives you're drawing? e.g. If you are drawing a lot of stripes, you can draw alternating rectangles of different colours (= 2n rectangles), or you can fill the entire background with one colour and then only draw the rectangles for the second colour (= n+1 rectangles). Reducing the number of individual calls to GDI+ methods can often provide significant gains, especially if you have fast graphics hardware.
If you draw any portion of the image more than once, consider caching it (render it into a bitmap and then blitting it to your final image when needed). The more complex this sub-image is, the more likely it is that caching it will pay off. For example, if you have a repeating pattern like a ruler, don't draw every ruler marking as a separate line - render a repeating section of the ruler (e.g. 10 lines or 50) and then blit this prerendered only a few times to draw the final ruler.
Similarly, avoid doing lots of unnecessary work (like many MeasureString calls for values that could be precalculated once or even approximated. Or if you're stepping through a lot of Y values, try to do it by adding an offset on each iteration rather than recaclualting the absolute position using mutliples every time).
Try to "batch" drawing to minimise the number of state changes and/or drawing method calls that are necessary - e.g. draw all the elements that are in one colour/texture/brush before you move on to the next colour. Use "batch" rendering calls (e.g. Draw a polyline primitive once rather than calling DrawLine 100 times).
If you're doing any per-pixel operations, then it's usually a lot faster to grab the raw image buffer and manipulate it directly than to call GetPixel/SetPixel methods.
And as you've already mentioned, you can turn off expensive operations such as anti-aliasing and font smoothing that won't be of any/much benefit in your specific case.
And of course, look at the code you're rendering with - profile it and apply the usual optimisations to help it flow efficiently.
Lastly, there comes a point where you should consider whether a hardware upgrade might be a cheap and effective solution - if you have a slow PC and a low end graphics card there may be a significant gain to be had by just buying a new PC with a better graphics card in it. Or if the images are huge, you may find a couple of GB more RAM eliminates virtual memory paging overheads. It may sound expensive, but there comes a point where the cost/benefit of new hardware is better than ploughing more money into additional work on optimisations (and their ever decreasing returns).
I have a few ideas:
Look at the code in Paint.net. It is an open source paint program written in C#. It could give you some good ideas. You could certainly do this in conjunction with ideas 2 and 3.
If the jobs need to be done in an "immediate" way, you could use something asynchronous to create the images. Depending on the scope of the overall application, you might even use something like NServiceBus to queue an image processing component with the task. Once the task is completed, the sending component would receive a notification via subscribing to the message published upon completion.
The task based solution is good for delayed processing. You could batch the creation of the images and use either the Task approach or something called Quartz.net (http://quartznet.sourceforge.net). It's an open source job scheduler that I use for all my time based jobs.
You can create a new Bitmap image and do LockBits(...), specifying the pixel format you want. Once you have the bits, if you want to use unmanaged code to draw into it, bring in the library, pin the data in memory, and use that library against it. I think you can use GDI+ against raw pixel data, but I was under the impression that System.Drawing is already a thin layer on top of GDI+. Anyways, whether or not I'm wrong about that, with LockBits, you have direct access to pixel data, which can be as fast or slow as you program it.
Once you're done with drawing, you can UnlockBits and viola you have the new image.
I have a simple scene with 64 ModelUIElement3D elements, every element3d Model is a model group with 8 simple geometries, every geometry contains 3 materials, diffuse, specular and emissive.
All materials use Solid Brushes since are the fastest.
The geometries, specular material and diffuse material are Freeze to improve performance.
The only thing not freeze are the emissive materials which I’ll be updating the brush with solid brushes periodically.
When I run the application and change the brush for only one emissive material from the 1536 materials in the screen (64*8*3) the performance goes down really really bad, and the FPS go down from 60 to 15FPS.
Even I have the solid brushes in a dictionary so they are cached and created once.
This change in the brush is to make the material “blink” (really it is a fast fade-in/fade-out) in the screen. The brush change in the emissive material is every 20ms.
I see changing the brush for one material or 500 materials doesn’t make any the difference in the performance.
I comment the line where I update the brush and I get a consistent 60FPS, when I uncomment the line the FPS goes down to +/-15FPS, tried 3 different machines, 4 cores/2 Cores, very good graphic cards or medium, almost doesn't make a difference, always between 15-25 FPS.
I’m using “Performance Profiling Tool for Windows Presentation Foundation” to measure the performance, and HW IRTs per frame doesn’t go above 1 at any moment. I see when I check the checkbox “dirty-region update” that the whole scene is render every 20ms just changing the brush for one small material.
I read that WPF viewport3d doesn’t support dirty region, so I’m assuming that the whole scene is render for a minimum change.
If that is the case, is there something I can do to improve that? Since I need to create an static scene with several thousand UIElements and 10 thousands materials, but the changes are going to be so minimal so I was expecting a good performance.
If the WHOLE scene is rendered every time without chance to do anything then it is useless think on WPF to create dynamic content and I have to go for another much more complex approach as DirectX.
Please any hint how to make this work is appreciated, since I can't believe it is not possible with WPF and is some mistake on my part.
Basically I need a big scene with minimal changes, those changes happen frequently in the order of 5 to 20ms and still get the 60FPS since there are few triangles material that really change.
Thanks,
Gustavo.
I cannot give a direct solution to your question. But here's some clue:
1536 materials seems too much for the simple business charting oriented WPF 3D. It is even necessary to carefully optimize if you used low-level API like Direct3D, because down into the graphics driver architecture it will hit the batching bottle-neck, especially for DirectX 9 level driver interface.
To get best performance, the only solution is to optimize case by case with low-level graphics API, with the knowledge of graphics hardware quirks. But for a high-level general purpose 3D API, it is simply not realistic to optimize for every case. It has make some assumption, e.g. the common usage should be navigating a static scene. So that it can optimize and cache the optimized command queue to get best performance. But if the scene is changed, the cache will be invalidate and thus need to re-build and re-optimize. So it is not the size of the viewport that caused FPS drop, but the optimization.
Usually, there's hinting mechanism, by which you tell it which sub-scene-graph changes while which is static, so that to help the underlying optimization. But it is only a hint. There are many cases which make it not viable to honor the hint. And it is hard to tell the cause because the internals are encapsulated.