Announcement

Collapse
No announcement yet.

Screen DC Slowdown Under Windows Vista

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Screen DC Slowdown Under Windows Vista

    Hi Folks,

    The following test code works fine under Windows XP and other OSs when %LOCALDC is set to zero (draw on the screen DC), but under Vista it is very slow. It is actually to do with the new "Aero" feature, because when Aero is disabled, the drawing is as fast as normal.

    I guess my question is: does anyone know of a workaround or switch to speed up the screen DC drawing when run in Vista's Aero mode? I need the whole screen DC as the actual code will need to draw outside of any windows.

    I have included the %LOCALDC flag to demonstrate the speed for Vista users.

    Code:
    #Compile Exe
    #Dim All
    #Include "WIN32API.INC"
    #Include "TYPES.INC"
    
    %THICKNESS = 5          ' Line thickness (must be 1 or greater).
    %STYLE = 0              ' Line style (0 solid, 1 animated).
    %LOCALDC = 1            ' DC mode (0 use screen DC, 1 Use dialog DC).
    Global GHDC As Dword    ' Device context.
    
    '------------------------------------------------------------------------------
    ' Draws a normal or animated border rectangle to specified DC.
    '------------------------------------------------------------------------------
    Function DrawOutline(ByVal hDC As THANDLE, _
                         ByVal rc As RECT, _
                         ByVal nThickness As TINT, _
                         ByVal nAnimated As TINT) As TINT
      Local i As TINT
      Local hPen As THANDLE
      Local hOldPen As THANDLE
      Local hBrush As THANDLE
      Local hOldBrush As THANDLE
      Local nDrawMode As TINT
      ' Must have DC and rectangle...
      If (hDC = %NULL) Or (IsRectEmpty(rc) <> %FALSE) Then Exit Function
      If nAnimated Then
         ' Draws a Win95 style animated border...
         For i = 1 To nThickNess
             DrawFocusRect hDc, rc
             InflateRect rc, 1, 1
         Next i
      Else
         ' Draws an old fashioned border...
         ' Select GDI objects, etc,
         hPen = CreatePen(%PS_INSIDEFRAME, 0, 0)
         hOldPen = SelectObject(hDC, hPen)
         hBrush = GetStockObject(%NULL_BRUSH)
         hOldBrush = SelectObject(hDC, hBrush)
         nDrawMode = SetROP2(hDC, %R2_NOT)
         For i = 1 To nThickNess
             ' Draw rectangle
             Rectangle hDC, rc.nLeft, rc.nTop, rc.nRight, rc.nBottom
             InflateRect rc, 1, 1
         Next i
         ' Restore DC
         SetROP2 hDC, nDrawMode
         SelectObject hDC, hOldBrush
         SelectObject hDC, hOldPen
         ' Delete GDI objects
         DeleteObject hPen
      End If
      Function = %TRUE
    End Function
     
    CallBack Function DLGPROC
      Select Case CbMsg
             Case %WM_INITDIALOG
                  ' Select DC to draw into...
                  #If %LOCALDC
                      '##### Very fast on vista, but will not draw outside of window...
                      gHDC = GetDC(CbHndl)
                  #Else
                      '##### All very slow on Vista...
                      'ghDC = GetDC(%NULL)
                      'ghDC = GetDC(GetDesktopWindow)
                      ghDC = CreateDC("DISPLAY", ByVal %NULL, ByVal %NULL, ByVal %NULL)
                  #EndIf
                 
             Case %WM_MOUSEMOVE
                  Static rcOld As RECT
                  Local rc As RECT
                  ' MAKE NEW
                  GETCURSORPOS ByVal VarPtr(RC) ' Only needs first 8 bytes (left/top).
                  #If %LOCALDC
                      ScreenToClient CbHndl, ByVal VarPtr(RC) ' Only needs first 8 bytes (left/top).
                  #EndIf
                  RC.NRIGHT = RC.NLEFT + 100
                  RC.NBOTTOM = RC.NTOP + 100
                  ' NO DUPES
                  If (RCOLD = RC) Then Exit Function
                  ' CLEAR OLD
                  DrawOutline ghDC, rcOld, %THICKNESS, %STYLE
                  ' DRAW NEW
                  DrawOutline ghDC, rc, %THICKNESS, %STYLE
                  ' SAVE NEW
                  RCOLD = RC
             Case %WM_DESTROY
                  #If %LOCALDC
                      ReleaseDC CbHndl, gHDC
                  #Else
                      DeleteDC gHDC
                  #EndIf
      End Select
    End Function
    
    Function PBMain
      Local HDLG As Dword
      Dialog New 0, "DRAG TEST FOR VISTA", , , 400, 300, %WS_OVERLAPPEDWINDOW To HDLG
      Dialog Show Modal HDLG Call DLGPROC
    End Function
    kgpsoftware.com | Slam DBMS | PrpT Control | Other Downloads | Contact Me

  • #2
    Kev,

    Auro does all sorts of special stuff , so likely it is buffering all writes to DC's so it can do its stuff in the background (like alphablending). Maybe drawing to the desktop DC puts more of a burden on it.

    One thing I did notice was your implimentation of getting a DC (in WM_INITDIALOG) and using it until WM_DESTROY is not a good design. It is not recommended.

    The only time I get a DC , stores it handle and use it until WM_DESTROY is a memory DC. Screen DC's are quite different. Windows often uses shared DC's among windows and to use a DC the way you do is not good.

    You should get the DC in your drawing code, draw with it and immediately free it.

    Try this:

    Code:
    ' test on Vista (Aero slows it down)
    #COMPILE EXE
    #DIM ALL
    #INCLUDE "WIN32API.INC"
    ' #Include "TYPES.INC"
    %THICKNESS = 5          ' Line thickness (must be 1 or greater).
    %STYLE = 0              ' Line style (0 solid, 1 animated).
    %LOCALDC = 0            ' DC mode (0 use screen DC, 1 Use dialog DC).
    GLOBAL GHDC AS DWORD    ' Device context.
    '------------------------------------------------------------------------------
    ' Draws a normal or animated border rectangle to specified DC.
    '------------------------------------------------------------------------------
    FUNCTION DrawOutline(BYVAL hDC AS LONG, _
                         BYVAL rc AS RECT, _
                         BYVAL nThickness AS LONG, _
                         BYVAL nAnimated AS LONG) AS LONG
      LOCAL i AS LONG
      LOCAL hPen AS LONG
      LOCAL hOldPen AS LONG
      LOCAL hBrush AS LONG
      LOCAL hOldBrush AS LONG
      LOCAL nDrawMode AS LONG
      ' Must have DC and rectangle...
      IF (hDC = %NULL) OR (IsRectEmpty(rc) <> %FALSE) THEN EXIT FUNCTION
      IF nAnimated THEN
         ' Draws a Win95 style animated border...
         FOR i = 1 TO nThickNess
             DrawFocusRect hDc, rc
             InflateRect rc, 1, 1
         NEXT i
      ELSE
         ' Draws an old fashioned border...
         ' Select GDI objects, etc,
         hPen = CreatePen(%PS_INSIDEFRAME, 0, 0)
         hOldPen = SelectObject(hDC, hPen)
         hBrush = GetStockObject(%NULL_BRUSH)
         hOldBrush = SelectObject(hDC, hBrush)
         nDrawMode = SetROP2(hDC, %R2_NOT)
         FOR i = 1 TO nThickNess
             ' Draw rectangle
             Rectangle hDC, rc.nLeft, rc.nTop, rc.nRight, rc.nBottom
             InflateRect rc, 1, 1
         NEXT i
         ' Restore DC
         SetROP2 hDC, nDrawMode
         SelectObject hDC, hOldBrush
         SelectObject hDC, hOldPen
         ' Delete GDI objects
         DeleteObject hPen
      END IF
      FUNCTION = %TRUE
    END FUNCTION
    CALLBACK FUNCTION DLGPROC
      SELECT CASE CBMSG
             CASE %WM_INITDIALOG
                  ' Select DC to draw into...
             CASE %WM_MOUSEMOVE
                  #IF %LOCALDC
                      '##### Very fast on vista, but will not draw outside of window...
                      gHDC = GetDC(CBHNDL)
                  #ELSE
                      '##### All very slow on Vista...
                      'ghDC = GetDC(%NULL)
                      'ghDC = GetDC(GetDesktopWindow)
                      ghDC = CreateDC("DISPLAY", BYVAL %NULL, BYVAL %NULL, BYVAL %NULL)
                  #ENDIF
                  
                  STATIC rcOld AS RECT
                  LOCAL rc AS RECT
                  ' MAKE NEW
                  GETCURSORPOS BYVAL VARPTR(RC) ' Only needs first 8 bytes (left/top).
                  #IF %LOCALDC
                      ScreenToClient CBHNDL, BYVAL VARPTR(RC) ' Only needs first 8 bytes (left/top).
                  #ENDIF
                  RC.NRIGHT = RC.NLEFT + 100
                  RC.NBOTTOM = RC.NTOP + 100
                  ' NO DUPES
                  IF (RCOLD = RC) THEN EXIT FUNCTION
                  ' CLEAR OLD
                  DrawOutline ghDC, rcOld, %THICKNESS, %STYLE
                  ' DRAW NEW
                  DrawOutline ghDC, rc, %THICKNESS, %STYLE
                  ' SAVE NEW
                  RCOLD = RC
                  #IF %LOCALDC
                      ReleaseDC CBHNDL, gHDC
                  #ELSE
                      DeleteDC gHDC
                  #ENDIF
                  
             CASE %WM_DESTROY
      END SELECT
    END FUNCTION
    FUNCTION PBMAIN
      LOCAL HDLG AS DWORD
      DIALOG NEW 0, "DRAG TEST FOR VISTA", , , 400, 300, %WS_OVERLAPPEDWINDOW TO HDLG
      DIALOG SHOW MODAL HDLG CALL DLGPROC
    END FUNCTION
    I don't know if this will improve the Vista performance or not, but it is the right way to draw (by using a DC and releasing it in the draw code).
    Chris Boss
    Computer Workshop
    Developer of "EZGUI"
    http://cwsof.com
    http://twitter.com/EZGUIProGuy

    Comment


    • #3
      DC's

      It could also be video memory use. I have a graphics engine written on a Win2K machine. It is 100% GDI, and cranks at 100FPS on 800Mhz Win2K, 512Mb Ram, ATI 7500 (32MB).

      I just recently bought a WinXP machine and its twice as fast but running at only 64FPS. All of the WinXP skins use alot of memory and apear to slow down GDI drawing.

      I suspect Vista is 10times worse. Its clearly a resource hog. The OS is using more of your PC than you/your apps. (in my opinion VISTA is useless)

      If M$ continues this path it will only get slower/more bloated.

      Tye
      Explorations v9.10 RPG Development System
      http://www.explore-rpg.com

      Comment


      • #4
        Chris,

        The screen DC storage does not make a difference as this is just some example code. It is Aero causing the slowdown but I do not know how to speed it up.

        Tyrone,

        The "dragging" action will be done directly by the user, so it doesn't matter really how much GFX % is being used. I know Vista is a hog but I need this problem solved.

        Somebody must have come across it before as there is a lot of code out there that draws to the screen DC and will have this slowdown. One app that doesn't slow down (not surprisingly!) is Microsoft's Wordpad utility. Dragging the bars around in Wordpad, which draws outside of it's window, works at the normal speed. I haven't yet tested the other MFC apps that also use the docking bar control (I think Wordpad is an MFC app). I will have to do a bit more research to see how they do it.
        kgpsoftware.com | Slam DBMS | PrpT Control | Other Downloads | Contact Me

        Comment


        • #5
          Kev,

          Did you try the modified version of code I posted above on Vista ?
          Chris Boss
          Computer Workshop
          Developer of "EZGUI"
          http://cwsof.com
          http://twitter.com/EZGUIProGuy

          Comment


          • #6
            Avoid using the obsolete GDI when dealing with DWM (Vista AERO)
            GDIPLUS is the way to go.

            And use the double buffer technic.
            Patrice Terrier
            www.zapsolution.com
            www.objreader.com
            Addons: GDImage.DLL 32/64-bit (Graphic library), WinLIFT.DLL 32/64-bit (Skin Engine).

            Comment


            • #7
              See the following links for the reason why GetDC(null) takes a performance hit.
              http://blogs.msdn.com/greg_schechter...02/588934.aspx
              http://blogs.msdn.com/nickkramer/arc...07/571162.aspx
              Dominic Mitchell
              Phoenix Visual Designer
              http://www.phnxthunder.com

              Comment


              • #8
                Hi all,

                Chris,
                Tried the code. Thanks but it makes no difference.

                Patrice,
                The code will be part of a custom control. If it was part of a main app, then I would go the GDIPlus route, but I do not want any dependencies for the new DLL (unless I could load and use GDIPlus if it's there already?).

                Dominic,
                Useful info. Thanks. I kinda thought that was the problem but I'm still curious about how MFC implemented the direct draw to screen as seen in Wordpad. It works really smooth.

                As a side note, the GDI might be 'obsolete' but I believe Microsoft have made a big mistake by causing these incompatibilities. They should, at the very least, have provided a workaround for this crappy VISTA slowdown.
                Last edited by Kev Peel; 22 Dec 2007, 03:01 AM.
                kgpsoftware.com | Slam DBMS | PrpT Control | Other Downloads | Contact Me

                Comment


                • #9
                  (unless I could load and use GDIPlus if it's there already?).
                  IF VISTA() = %TRUE THEN
                  UseGDIPLUS
                  ELSE
                  UseGDI
                  END IF

                  I posted several examples on Jose Roca's forum related to the use of VISTA DWM (Device Window Manager), and I showed how to write on the composited GLASS section.

                  Once you know how to use it, you will see all the benefit of using DWM.

                  As an example, download the Crystal demo there:
                  http://www.jose.it-berater.org/smffo...p?topic=1372.0
                  and see how fast it could be with VISTA while in AERO GLASS mode.
                  Patrice Terrier
                  www.zapsolution.com
                  www.objreader.com
                  Addons: GDImage.DLL 32/64-bit (Graphic library), WinLIFT.DLL 32/64-bit (Skin Engine).

                  Comment


                  • #10
                    but I'm still curious about how MFC implemented the direct draw to screen as seen in Wordpad.
                    Microsofts' Wordpad? Which command accesses this feature?
                    Dominic Mitchell
                    Phoenix Visual Designer
                    http://www.phnxthunder.com

                    Comment


                    • #11
                      Move the toolbars in Wordpad. The rectangle draws on the screen.

                      Since I have VS 2005, I looked at MFC's C++ source (dockcont.cpp) it uses LockWindowUpdate and GetDCEx (DCX_WINDOW, DCX_CACHE, DCX_LOCKWINDOWUPDATE flags) with the desktop window but it doesn't seem to have any effect. Strange.

                      I'm missing something, or the "CWnd" object MFC uses is doing more "behind the scenes"
                      kgpsoftware.com | Slam DBMS | PrpT Control | Other Downloads | Contact Me

                      Comment


                      • #12
                        OK. I also do the same in Phoenix with the docking container.
                        I will have to test on Vista to see if I have the same problem.
                        In my case, if there is a problem I can move the full docking window when it is being dragged, and draw a rectangle iff the container is over or near the target area of the host window(GetDC(hWndHost)).
                        Dominic Mitchell
                        Phoenix Visual Designer
                        http://www.phnxthunder.com

                        Comment


                        • #13
                          Kev,

                          Dominics post above (to msdn blogs) answers the question.

                          Drawing within a window is fine, but attempting to draw on the screen DC, when Aero is running is problematic. XORing requires bytes from video memory to be read when drawing and it appears that Aero doesn't like this.

                          There is a video manager of sorts in Aero which buffers all windows so drawing on a window DC, you are never drawing on the screen DC. Aero buffers the draws and then it takes care of putting the image on the screen. This allows all the alphablending tricks it does.

                          By attempting to draw on the screen (or desktop), you are trying to work around the video manager and obviously it does not like it.

                          Drawing on a window DC is fine though.

                          So the question is, how to get around this quirk of Aero ?

                          Don't draw on the screen directly (ie. GetDC(NULL)).

                          You can create a custom window class for the rectangular image you are trying to move around. This window can use a region to define the visible area, so the rest is transparent. When the window is resized, simply generate a new window region to match.

                          I have used this technique successfully with my drag handle control. It has 8 drag handles (one each on the four corners and four sides). When the control is resized, the control creates a new region on the fly and then redraws itself.

                          Using this technique you only draw on an actual window DC. The region is what allows it to appear transparent. By keeping it at the top of the zorder, it will stay above all other windows.
                          Last edited by Chris Boss; 22 Dec 2007, 02:55 PM.
                          Chris Boss
                          Computer Workshop
                          Developer of "EZGUI"
                          http://cwsof.com
                          http://twitter.com/EZGUIProGuy

                          Comment


                          • #14
                            You can create a custom window class for the rectangular image you are trying to move around.
                            This window can use a region to define the visible area, so the rest is transparent. When the
                            window is resized, simply generate a new window region to match.
                            I use the same technique to allow floating windows to dock in Pheonix when the "No Background"
                            option is set. The "No Background" option allows the user to see other applications underneath
                            Phoenix. As Chris points out, it works very well, and resizing a rectangular hole is very fast.
                            Dominic Mitchell
                            Phoenix Visual Designer
                            http://www.phnxthunder.com

                            Comment


                            • #15
                              Thanks, but I have managed to get it working without using an additional window. Basically, you lock the desktop window and get it's DC, then draw onto it using XOR regions and PatBlt. I was using the API's DrawFocusRect() and Rectangle() functions, with a NULL brush - this method was very slow.

                              I found out how Microsoft did it by looking at the functions in MFC's dockcont.cpp. I implemented their method and it now works fine with any size line width and no speed penalty under Vista.
                              kgpsoftware.com | Slam DBMS | PrpT Control | Other Downloads | Contact Me

                              Comment


                              • #16
                                I was using the API's DrawFocusRect() and Rectangle() functions, with a NULL brush
                                In my case I use a path and FillPath, so I am probably OK.
                                Dominic Mitchell
                                Phoenix Visual Designer
                                http://www.phnxthunder.com

                                Comment


                                • #17
                                  Finally got around to posting my solution

                                  http://www.powerbasic.com/support/pbforums/showthread.php?p=292336
                                  kgpsoftware.com | Slam DBMS | PrpT Control | Other Downloads | Contact Me

                                  Comment

                                  Working...
                                  X