High Speed 2D Graphics with DirectDraw

jerome

Newcomer
Joined
Oct 15, 2003
Messages
7
Location
Adelaide, Australia
Hi everyone.

I am trying to write a .NET application that displays some simple 2D graphics in a window. The graphics that are displayed consist of many individual lines of varying shades of green, depending on the data received by the application.

All of this is working, but the program consumes nearly all of my CPU. I really need to improve the performance of my code and I have identified that the bottleneck occurs when I call DrawLine on my offscreen surface for each of the many lines that are displayed.

As extra information, I am refreshing the display at 50Hz to get a nice smooth display. I have a NVidia Riva TNT2 Model 64 video card but it does not seem to improve the performance over using the display chipset on the motherboard.

Any ideas on how I can improve the performance of the DrawLine command would be great.

Bye.
 
Without seeing any actual code, one can only assume that the problem is with your outdated video card.
 
Here is the code that I am using. A lot of the code has been removed but I think that I have kept all of the relevant parts. Almost all of the CPU time is spent doing the DrawLines at the end of the code.

C#:
public class DXTest
{
 int[,] ValuesToDraw;
 private Device device;
 private Surface primary;
 private Surface offscreen;

 public void Setup()
 {
  device = new Microsoft.DirectX.DirectDraw.Device();
  device.SetCooperativeLevel(this, CooperativeLevelFlags.Normal);
  CreatePrimarySurface(); 
  CreateOffscreenSurface();
 }

 private void CreatePrimarySurface()
 {
  SurfaceDescription desc = new SurfaceDescription();
  desc.SurfaceCaps.PrimarySurface = true; 
  primary = new Surface(desc, device);
  Clipper pClipper = new Clipper(device); 
  pClipper.Window = OIApp.OIGUI.GetSelectedTabPage(); 
  primary.Clipper = pClipper;
 }

 private void CreateOffscreenSurface()
 {
  SurfaceDescription desc = new SurfaceDescription(); 
  desc.SurfaceCaps.OffScreenPlain = true; 
  desc.Width = this.ClientRectangle.Width; 
  desc.Height = this.ClientRectangle.Height; 
  offscreen = new Surface(desc, device); 

  Clipper oClipper = new Clipper(device); 
  Rectangle oClipRect = new Rectangle(0,
   0,
   this.ClientRectangle.Width,
   this.ClientRectangle.Height); 
  oClipper.ClipList = new Rectangle[] { oClipRect }; 
  offscreen.Clipper = oClipper;
 }

 public void Run(Object state)
 {
  for(int i=0; i<360; i++)
  {
   for(int j=0; j<10; j++)
   {
    offscreen.ForeColor = Color.FromArgb(0,250,0);
    offscreen.DrawLine(xs+xt, -ys+yt, xe+xt, -ye+yt);
   }
  }
	 
  primary.Draw(this.ClientRectangle, offscreen, DrawFlags.Wait);
 }
}
 
Possible Reason for Slow DrawLine

I've been looking at this problem some more and I have come to the following conclusion. Perhaps someone can say whether this is right or wrong?

The DirectDraw DrawLine() function calls the getDC() and releaseDC() as part of it's operation. As a consequence, multiple calls to DrawLine() involve multiple calls to getDC() and releaseDC(), which make it VERY slow.

I have worked around this using the GDI calls moveTo() and LineTo() for a massive performance boost, up to 20x faster in my worst case.

Any other ideas out there??
 
Changing the DrawFlags from Wait to DoNoWait only affects the drawing of the offscreen surface to the primary surface. This may provide a small performance improvement (it's a bit hard to see any change using the Task Manager) but the majority of the time is spent on the DrawLine function, which is performed on the offscreen surface.
 
Well.. the only other improvement that I see would be putting the call to ForeColor outside of the two for loops (calling it once before the looping). Whether or not that'd improve your speed, I don't know, but I don't see anything else.
 
I actually use the Forecolor to change the color of the lines that I draw in my application, but this is not fully shown in the code that I submitted. However, from my testing it is definitely the call to DrawLine that takes up most of the execution time.

What are your thoughts on my guess that the DrawLine function calls the getDC and releaseDC at the next level down in the code??
 
unless the code specifically sleep()s or does something of an idle loop, this is what happens (100% cpu). look at any game, it will consume 100% cpu even if you turn down all the details. in order to make it take less cpu, you have to implement FIXED FRAMERATE. that is, you go idle if you are too fast.
 
Although it is nto obvious from the code that is posted, the program is frame rate limited, to 50 frames per second in fact.

Because the DrawLine function takes so long, the software is unable to execute all of the required DrawLine functions within 20ms and the program falls behind, i.e. doesn't display the line data as quickly as it should.

As I have said in a previous post, the DrawLine command is using the CPU.
 
sorry ...

well then,
can't you pre-draw all lines to a buffer before and render the whole buffer in one go? or does the lines differe every time?
 
another tip is forgetting about directdraw and usem direct3d for hardware accelerated line drawing

bet you could draw thousands on your old gfx card and still maintain framerate :)
 
Unfortunately, the lines can differ for each frame.

I also think that Direct3D would provide the required speed, but I have been trying to avoid it as it will take some time for me to learn how to use it properly.

At this stage, I am getting pretty good performance from using the GDI calls MoveTo and LineTo.
 
Aha! I think I have the solution now that I've had some experience programming in DD.

I noticed that you set a clipper to your offscreen (backbuffer) surface. NO! Bad. Evil. A clipper should only be used on the primary surface. You should make your own clipping routines for the backbuffer.

Comment out this block of code and give it a go, I bet you'll gain quite a bit of performance;

C#:
  Clipper oClipper = new Clipper(device); 
  Rectangle oClipRect = new Rectangle(0,
   0,
   this.ClientRectangle.Width,
   this.ClientRectangle.Height); 
  oClipper.ClipList = new Rectangle[] { oClipRect }; 
  offscreen.Clipper = oClipper;
 
Back
Top