Some confusion about drawing in .NET
Some confusion about drawing in .NET
Some confusion about drawing in .NET
Some confusion about drawing in .NET
Some confusion about drawing in .NET
Some confusion about drawing in .NET Some confusion about drawing in .NET Some confusion about drawing in .NET Some confusion about drawing in .NET Some confusion about drawing in .NET Some confusion about drawing in .NET Some confusion about drawing in .NET Some confusion about drawing in .NET
Some confusion about drawing in .NET Some confusion about drawing in .NET
Some confusion about drawing in .NET
Go Back  Xtreme Visual Basic Talk > > > Some confusion about drawing in .NET


Reply
 
Thread Tools Display Modes
  #1  
Old 06-09-2008, 10:05 AM
darkforcesjedi's Avatar
darkforcesjediSome confusion about drawing in .NET darkforcesjedi is offline
Trust me, I'm an

* Expert *
 
Join Date: Apr 2001
Location: In ur base, pwnin d00dz
Posts: 1,964
Default Some confusion about drawing in .NET


My experience in .NET is extremely limited. In the past I've tutored people in the college of management at my university (using my VB6 experience) where they used VB.NET in one of their degree programs, which is where almost all of my .NET experience comes from. I wasn't able to take the courses myself as they were restricted to the students in the college of management.

I've been having some difficulties with drawing that I haven't been able to resolve on my own. To get some experience in this regard I started working on a clone of the classic game Breakout.

All my drawing is done in the Paint event of the form. I have an array of bricks and an array of balls (which contains information about the position and speed of each ball). The form is double-buffered.

I create a second thread that runs the game loop. Each time it updates the position of any object, I call the form's Invalidate method, passing the bounding box Rectangle of each ball's initial location. Then I call Invalidate again using the ball's final location.

The problem with this is that the ball flickers because (apparently) the screen is updated between all calls to Invalidate(). In each paint event I only draw the geometry that falls within the clip rectangle. I'd like to minimize the amount of drawing I do because it takes considerably longer than the actual game processing (20ms versus 1 ms), and if I draw too much the screen loses sync with the game thread (only by a tiny bit, only noticable when you miss the ball -- your turn appears to end before the ball actually reaches the bottom of the screen by perhaps a few hundredths of a second).

If I only call Invalidate() once using the rectangle that encloses the initial and final location of the ball, there is no more flicker, but now I'm redrawing a considerably larger area. When I'm drawing a number of simultaneously moving objects, this region approaches the size of the entire form, when I only really need to redraw about 5-10% of it.

What I would like to do is draw in the back-buffer during each game loop iteration, then in the paint event flip the relevant portion of the back-buffer to the screen (which is essentially how I handled drawing in VB6). How would this be handled in .NET? Is it possible to call Invalidate() with several rectangles, but only update the screen after all of the paint events finish?

I know it's possible to create my own backbuffer and draw on the form outside the paint event, but the .NET documentation indicates that this is no longer necessary or appropriate.

Clearly I'm doing something wrong, but what is the right way to move an object from one location to another? Surely it doesn't require redrawing everything between the initial and final locations.
__________________
To err is human; to debug, divine.
Reply With Quote
  #2  
Old 06-09-2008, 10:20 AM
AtmaWeapon's Avatar
AtmaWeaponSome confusion about drawing in .NET AtmaWeapon is offline
Fabulous Florist

Forum Leader
* Guru *
 
Join Date: Feb 2004
Location: Austin, TX
Posts: 9,500
Default

Iceplug wrote a tutorial about using backbuffers a while back; I doubt much has changed since then. The only trouble I see with this approach is he is redrawing the entire screen at once; you seem to have indicated this will be too slow for you, but there is a solution.

You can probably get away with it, though. You're already redrawing the geometry in the particular areas that you know have changed, so you should probably just preface any drawing by erasing the parts of the image you're about to draw on.
__________________
.NET Resources
My FAQ threads | Tutor's Corner | Code Library
I would bet money 2/3 of .NET questions are already answered in one of these three places.
Reply With Quote
  #3  
Old 06-09-2008, 11:10 AM
darkforcesjedi's Avatar
darkforcesjediSome confusion about drawing in .NET darkforcesjedi is offline
Trust me, I'm an

* Expert *
 
Join Date: Apr 2001
Location: In ur base, pwnin d00dz
Posts: 1,964
Default

I'm aware of the benefits of using back-buffers and how to manually implement them in .NET. My only qualm is that the documentation seems to indicate that I shouldn't be doing it this way.

I could just draw on a backbuffer in the game thread and create a new thread that flips it to the screen on a timer (maybe 20 fps), of course making sure to wait for the game thread to finish drawing the current frame. That way I wouldn't have to worry about the screen being out of synch with the game thread.
__________________
To err is human; to debug, divine.
Reply With Quote
  #4  
Old 06-09-2008, 11:33 AM
AtmaWeapon's Avatar
AtmaWeaponSome confusion about drawing in .NET AtmaWeapon is offline
Fabulous Florist

Forum Leader
* Guru *
 
Join Date: Feb 2004
Location: Austin, TX
Posts: 9,500
Default

The documentation discourages this method because for most Windows Forms tasks it's overkill and will increase memory overhead and complexity with no visible performance gains. You're in an advanced use case, so you can ignore the statements that discourage it. Bending the rules isn't so bad when there's no way to do things correctly and stay within the rules
__________________
.NET Resources
My FAQ threads | Tutor's Corner | Code Library
I would bet money 2/3 of .NET questions are already answered in one of these three places.
Reply With Quote
  #5  
Old 06-11-2008, 06:26 PM
darkforcesjedi's Avatar
darkforcesjediSome confusion about drawing in .NET darkforcesjedi is offline
Trust me, I'm an

* Expert *
 
Join Date: Apr 2001
Location: In ur base, pwnin d00dz
Posts: 1,964
Default

Quote:
Originally Posted by AtmaWeapon View Post
The documentation discourages this method because for most Windows Forms tasks it's overkill and will increase memory overhead and complexity with no visible performance gains. You're in an advanced use case, so you can ignore the statements that discourage it. Bending the rules isn't so bad when there's no way to do things correctly and stay within the rules
I created a sample project, which apparently demonstrates that GDI+ is crap.

Put the following code in a new form (Form1) and run it:

Code:
Imports System.Drawing
Imports System.Threading
Imports System.Diagnostics

Public Class Form1

    Declare Function GetTickCount Lib "kernel32.dll" () As Integer

    Dim Primary As Graphics
    Dim BackBuffer As Graphics
    Dim BBBitmap As Image

    Dim DrawThread As Thread
    Dim MainLoop As Thread

    Dim Loc As Point, oldLoc As Point

    Dim bQuit As Boolean, bRunning As Boolean

    Private Sub Form1_Activated(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Activated
        If Not bRunning Then
            bRunning = True
            DrawThread = New Thread(AddressOf DrawLoop)
            MainLoop = New Thread(AddressOf GameLoop)
            MainLoop.Start()
            DrawThread.Start()
        End If
    End Sub

    Private Sub Form1_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
        bQuit = True
        DrawThread.Join()
        MainLoop.Join()

        Primary.Dispose()
        BackBuffer.Dispose()
        BBBitmap.Dispose()

        Primary = Nothing
        BackBuffer = Nothing
    End Sub

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Primary = Me.CreateGraphics
        BBBitmap = New Bitmap(Me.ClientRectangle.Width, Me.ClientRectangle.Height, Imaging.PixelFormat.Format24bppRgb)
        BackBuffer = Graphics.FromImage(BBBitmap)
        BackBuffer.FillRectangle(Brushes.White, 0, 0, BBBitmap.Width, BBBitmap.Height)
    End Sub


    Private Sub GameLoop()
        Dim LastUpdate As Long
        Dim FirstUpdate As Long
        LastUpdate = GetTickCount
        FirstUpdate = LastUpdate
        Do Until bQuit
            If (GetTickCount - LastUpdate) / 1000 >= 0.005 Then
                With Loc
                    .X = (Me.ClientRectangle.Width - 20) / 2 * (1 + Math.Sin((GetTickCount - FirstUpdate) / 100)) + 10
                    .Y = (Me.ClientRectangle.Height - 20) / 2 * (1 + Math.Cos((GetTickCount - FirstUpdate) / 100)) + 10
                End With
                LastUpdate = GetTickCount
            End If
            Thread.Sleep(1)
        Loop
    End Sub

    Private Sub DrawLoop()
        Dim LastUpdate As Long
        Dim t As Decimal
        Dim frametimes(1) As Long
        Dim i As Integer

        Dim f As Font = New Font("Lucida Console", 10)
        Dim s As String

        LastUpdate = GetTickCount
        Do Until bQuit
            If (GetTickCount - LastUpdate) / 1000 >= 0.01 Then
                i = 1 - i
                frametimes(i) = LastUpdate
                LastUpdate = GetTickCount
                BackBuffer.FillRectangle(Brushes.White, oldLoc.X - 8, oldLoc.Y - 8, 16, 16)
                BackBuffer.FillRectangle(Brushes.White, 0, 0, 150, 20)
                oldLoc = Loc
                BackBuffer.FillEllipse(Brushes.Black, oldLoc.X - 8, oldLoc.Y - 8, 16, 16)
                s = Format(1000.0# / (frametimes(i) - frametimes(1 - i)), "##,##0.00") & " fps"
                BackBuffer.DrawString(s, f, Brushes.Red, 0, 0)
                Primary.DrawImage(BBBitmap, 0, 0)
            End If
            Thread.Sleep(10)
        Loop
    End Sub
End Class

It absolutely will not draw faster than 21 fps, even if I comment out all the backbuffer drawing. But wait... click and drag on the title bar and suddenly it's going 70 fps. Release the title bar -> back to 21 fps.

This is at least 10 times slower than GDI in VB6. What's going on here? Am I missing something? Why does it draw faster when I'm clicking on the titlebar (of any application)? Why does the CPU usage decrease by a factor of 10 when clicking on the titlebar as well?
__________________
To err is human; to debug, divine.
Reply With Quote
  #6  
Old 06-11-2008, 07:43 PM
AtmaWeapon's Avatar
AtmaWeaponSome confusion about drawing in .NET AtmaWeapon is offline
Fabulous Florist

Forum Leader
* Guru *
 
Join Date: Feb 2004
Location: Austin, TX
Posts: 9,500
Default

No idea. GDI was made for drawing buttons and text boxes. If you want performance, perhaps you should look into DirectX?

One thing I'm noticing is you're creating the font on every drawing operation; Fonts are unmanaged Kernel objects and are thus slow. You might get a big boost out of caching that Font object. You declare the same one every time anyway, so why not cache it?

I'll see if I can't get another expert to look into this thread, there's likely plenty of people more familiar with the details of GDI+ than I.
__________________
.NET Resources
My FAQ threads | Tutor's Corner | Code Library
I would bet money 2/3 of .NET questions are already answered in one of these three places.
Reply With Quote
  #7  
Old 06-11-2008, 07:58 PM
darkforcesjedi's Avatar
darkforcesjediSome confusion about drawing in .NET darkforcesjedi is offline
Trust me, I'm an

* Expert *
 
Join Date: Apr 2001
Location: In ur base, pwnin d00dz
Posts: 1,964
Default

I'm not creating the font every frame. I create the font once and it persists as long as "DrawLoop" is running in the "DrawThread" thread. The DrawLoop subroutine doesn't exit til you close the form.
__________________
To err is human; to debug, divine.
Reply With Quote
  #8  
Old 06-11-2008, 09:04 PM
Cerian Knight's Avatar
Cerian KnightSome confusion about drawing in .NET Cerian Knight is offline
Polymath (in disciplina)

Super Moderator
* Expert *
 
Join Date: May 2004
Location: Michigan
Posts: 4,200
Default

The example code on Win2K PIII 800MHz with integrated graphics runs at 100fps, with dips to 50fps. It makes no difference if I click on any title bar. I am set to boost foreground applications, but that seemed to make little difference here.
__________________
I got all the answers wrong on the GLAT, apparently even #9 (where I put a period in the middle of the box and labeled it 'singularity ripe for rapid inflation').
Reply With Quote
  #9  
Old 06-12-2008, 06:50 AM
darkforcesjedi's Avatar
darkforcesjediSome confusion about drawing in .NET darkforcesjedi is offline
Trust me, I'm an

* Expert *
 
Join Date: Apr 2001
Location: In ur base, pwnin d00dz
Posts: 1,964
Default

What size form did you test it on? If I shrink the form to about 300x150 it's much faster. The performance I mentioned was on a form that's 600x400.

I wonder what in my configuration could be causing the problem. A similar application written in VB6 runs at around 400 fps (if I remove the frame time check) using a single thread.

I'm using VB.NET 2008 with .NET Framework 3.5. I don't know if that makes a difference. Windows XP Media Center Edition 2005 SP2, NVidia GEForce 8600 graphics card with 512 MB of RAM.
__________________
To err is human; to debug, divine.
Reply With Quote
  #10  
Old 06-12-2008, 09:11 AM
AtmaWeapon's Avatar
AtmaWeaponSome confusion about drawing in .NET AtmaWeapon is offline
Fabulous Florist

Forum Leader
* Guru *
 
Join Date: Feb 2004
Location: Austin, TX
Posts: 9,500
Default

Ahh, sorry about that. I had misinterpreted DrawLoop and assumed you were calling it every time you wanted to draw; I got to the font declaration and figured that was a problem.

I get a steady 62.5 FPS on a WinXP P4 Core Duo 1.86Ghz and I'm running some business level nVidia card. The form's size does not seem to affect this.

Keep in mind that I think GDI is mostly not hardware accelerated (this is something I wanted someone else to confirm) so you're rendering entirely in software; CPU speed's probably going to be your big enemy.
__________________
.NET Resources
My FAQ threads | Tutor's Corner | Code Library
I would bet money 2/3 of .NET questions are already answered in one of these three places.
Reply With Quote
  #11  
Old 06-12-2008, 10:07 AM
Cerian Knight's Avatar
Cerian KnightSome confusion about drawing in .NET Cerian Knight is offline
Polymath (in disciplina)

Super Moderator
* Expert *
 
Join Date: May 2004
Location: Michigan
Posts: 4,200
Default

Quote:
Originally Posted by darkforcesjedi View Post
What size form did you test it on?
I'll try a larger form when I get home, but here at work using 2005 Express on (4 year old) Win2K P4 2.6GHz w/integrated graphics I get about 60-65 fps regardless of form size.

OS, CPU/motherboard/chipset, video driver, hardware acceleration setting or bit-depth might be factors here, but there is no question that the frame rates are lower in .NET. If hardware acceleration is enabled, then GDI drawing should be able to use BitBlt, etc.

Here is an example in VB6 that pushes the envelope (yes, I know 1000fps is overkill) without moving to DirectX.
http://www.xtremevbtalk.com/showpost...9&postcount=17
__________________
I got all the answers wrong on the GLAT, apparently even #9 (where I put a period in the middle of the box and labeled it 'singularity ripe for rapid inflation').

Last edited by Cerian Knight; 06-12-2008 at 10:14 AM.
Reply With Quote
  #12  
Old 06-12-2008, 01:23 PM
darkforcesjedi's Avatar
darkforcesjediSome confusion about drawing in .NET darkforcesjedi is offline
Trust me, I'm an

* Expert *
 
Join Date: Apr 2001
Location: In ur base, pwnin d00dz
Posts: 1,964
Default

I can't find a reason why it rund so poorly on my computer. 62.5 is about what I get (flickers between that and 67) when I click and hold the title bar, even if I stretch the window to 1920x1080. I have an idea what may be causing the issue though. I'm going to try disabling the NVidia control panel and see if that helps. If I recall correctly there are features in it that are deactivated when dragging windows.

The reason the framerate doesn't exceed the ~67 fps limit is because I'm using a low resolution timer (GetTickCount), which on many machines has a resolution of 15 ms. On one of my old machines GetTickCount had a resolution of 1ms, so it was sufficient for a fixed 100fps framerate.

The QueryPerformanceCounter and QueryPerformanceFrequency APIs both raise memory access violation errors when I try to use them in .NET. How do I get better than 15ms resolution? System.Diagnostics.PerformanceCounter looked promising, but I can't find an example of using it as a timer. All I can find is creating counters for windows performance logs. I tried Now.Ticks, but that seems to have the same resolution as gettickcount.
__________________
To err is human; to debug, divine.
Reply With Quote
  #13  
Old 06-12-2008, 03:03 PM
Cerian Knight's Avatar
Cerian KnightSome confusion about drawing in .NET Cerian Knight is offline
Polymath (in disciplina)

Super Moderator
* Expert *
 
Join Date: May 2004
Location: Michigan
Posts: 4,200
Default

You are correct about the tick resolution. I forgot that here at work I have HT/Multiprocessor enabled, thus 15ms instead of 10ms. You might be on the right track with the NVidia software being the culprit. Unfortunately, I have not used QPC (or equivilent namespace) on .NET, but I will give it a shot when I get a chance.
__________________
I got all the answers wrong on the GLAT, apparently even #9 (where I put a period in the middle of the box and labeled it 'singularity ripe for rapid inflation').
Reply With Quote
  #14  
Old 06-12-2008, 03:51 PM
darkforcesjedi's Avatar
darkforcesjediSome confusion about drawing in .NET darkforcesjedi is offline
Trust me, I'm an

* Expert *
 
Join Date: Apr 2001
Location: In ur base, pwnin d00dz
Posts: 1,964
Default

No dice. Disabling the nvidia software didn't change a thing. I'm running windows update now (not SP3 though)
__________________
To err is human; to debug, divine.
Reply With Quote
  #15  
Old 06-12-2008, 06:08 PM
darkforcesjedi's Avatar
darkforcesjediSome confusion about drawing in .NET darkforcesjedi is offline
Trust me, I'm an

* Expert *
 
Join Date: Apr 2001
Location: In ur base, pwnin d00dz
Posts: 1,964
Default

I just commented out the line that draws the backbuffer to the screen and instead use a D3D sprite to draw the FPS to the screen. I'm drawing the backbuffer at over 3,000 FPS (!) using GDI+, but I can't copy it to the screen faster than 21 fps???
__________________
To err is human; to debug, divine.
Reply With Quote
  #16  
Old 06-13-2008, 12:34 PM
Cerian Knight's Avatar
Cerian KnightSome confusion about drawing in .NET Cerian Knight is offline
Polymath (in disciplina)

Super Moderator
* Expert *
 
Join Date: May 2004
Location: Michigan
Posts: 4,200
Default

Perhaps it is because the source and destination pixel formats are different so they are processed behind the scenes before the forground copy can occur. There is a workaround (and a reference to a better solution, but I haven't found it). I'm having trouble pulling up the original on MSDM Forum, so here is a Google cache link:
http://209.85.215.104/search?q=cache...nk&cd=10&gl=us
__________________
I got all the answers wrong on the GLAT, apparently even #9 (where I put a period in the middle of the box and labeled it 'singularity ripe for rapid inflation').
Reply With Quote
  #17  
Old 06-13-2008, 12:46 PM
darkforcesjedi's Avatar
darkforcesjediSome confusion about drawing in .NET darkforcesjedi is offline
Trust me, I'm an

* Expert *
 
Join Date: Apr 2001
Location: In ur base, pwnin d00dz
Posts: 1,964
Default

... What format is the form on the screen? I expected it would be Format24bppRgb.
__________________
To err is human; to debug, divine.
Reply With Quote
  #18  
Old 06-13-2008, 01:23 PM
AtmaWeapon's Avatar
AtmaWeaponSome confusion about drawing in .NET AtmaWeapon is offline
Fabulous Florist

Forum Leader
* Guru *
 
Join Date: Feb 2004
Location: Austin, TX
Posts: 9,500
Default

What if your graphics card is set to 32-bit color? Personally on a modern machine I'd imagine Format32bppArgb, but it'd be interesting to find out if there's a way to tell.
__________________
.NET Resources
My FAQ threads | Tutor's Corner | Code Library
I would bet money 2/3 of .NET questions are already answered in one of these three places.
Reply With Quote
Reply


Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off

Forum Jump

Advertisement:





Free Publications
The ASP.NET 2.0 Anthology
101 Essential Tips, Tricks & Hacks - Free 156 Page Preview. Learn the most practical features and best approaches for ASP.NET.
subscribe
Programmers Heaven C# School Book -Free 338 Page eBook
The Programmers Heaven C# School book covers the .NET framework and the C# language.
subscribe
Build Your Own ASP.NET 3.5 Web Site Using C# & VB, 3rd Edition - Free 219 Page Preview!
This comprehensive step-by-step guide will help get your database-driven ASP.NET web site up and running in no time..
subscribe
Some confusion about drawing in .NET
Some confusion about drawing in .NET
Some confusion about drawing in .NET Some confusion about drawing in .NET
Some confusion about drawing in .NET
Some confusion about drawing in .NET
Some confusion about drawing in .NET Some confusion about drawing in .NET Some confusion about drawing in .NET Some confusion about drawing in .NET Some confusion about drawing in .NET Some confusion about drawing in .NET Some confusion about drawing in .NET
Some confusion about drawing in .NET
Some confusion about drawing in .NET
 
Some confusion about drawing in .NET
Some confusion about drawing in .NET
 
-->