Jump to content
Sign in to follow this  
UEZ

Slow GDI+ performance on some (XP?) machines

Recommended Posts

UEZ

Hi,

why is e.g. _GDIPlus_GraphicsClear($backbuffer, 0x9A000000) in the example below extremly slow on some machines?

#include <GuiConstantsEx.au3>
#include <GDIPlus.au3>
#include <Array.au3>

Opt('MustDeclareVars', 1)
Global $hGUI, $hWnd, $hGraphic, $width, $height
$width = @DesktopWidth * 0.75
$height = @DesktopHeight * 0.75
Global Const $explosion_step = 6, $explosion_length = ($width + $height) / 8, $explosion_max_amount = 20, $explosion_max_particle = 20
Global $explosion_coordinate[$explosion_max_particle][6 * $explosion_max_amount] ; on/off, x, y, vx, vy, v
Global $explosion_amount = 0
; Create GUI
$hGUI = GUICreate("Explosions (from AUTOITEROIDS) using GDI+ by UEZ 2009!", $width, $height)
$hWnd = WinGetHandle($hGUI)
_GDIPlus_Startup()
Global $graphics = _GDIPlus_GraphicsCreateFromHWND($hwnd)
Global $bitmap = _GDIPlus_BitmapCreateFromGraphics($width, $height, $graphics)
Global $backbuffer = _GDIPlus_ImageGetGraphicsContext($bitmap)
_GDIPlus_GraphicsClear($backbuffer)
_GDIPlus_GraphicsSetSmoothingMode($backbuffer, 4)
Global $pen_size = 1
Global $pen_color = 0xAFFF8070
Global $pen = _GDIPlus_PenCreate($pen_color, $pen_size)
GUISetState()

Do
    _GDIPlus_GraphicsClear($backbuffer, 0x9A000000)
    If Mod(Random(1, 10, 1), 9) >= 5 Then ;
        Explosion_Init(Random(50, $width - 50, 1), Random(50, $height - 50, 1))
    EndIf
    Explosion()
    _GDIPlus_GraphicsDrawImageRect($graphics, $bitmap, 0, 0, $width, $height)
Until Not Sleep(30) Or GUIGetMsg() = $GUI_EVENT_CLOSE

; Clean up resources
_GDIPlus_PenDispose($pen)
_GDIPlus_BitmapDispose($bitmap)
_GDIPlus_GraphicsDispose($graphics)
_GDIPlus_GraphicsDispose($backbuffer)
_GDIPlus_Shutdown()



Func Explosion_Init($ex, $ey) ;initialise explosion
    For $o = 0 To $explosion_step * ($explosion_max_amount - 1) Step $explosion_step ;fill array with coordinate of hit object
        If $explosion_coordinate[0][$o] <> 1 Then
            $explosion_coordinate[0][$o] = 1
            $explosion_coordinate[0][$o + 1] = $ex ;save x coordinate
            $explosion_coordinate[0][$o + 2] = $ey ;save x coordinate
            For $n = 0 To $explosion_max_particle - 1
                $explosion_coordinate[$n][$o + 1] = $explosion_coordinate[0][$o + 1] ;duplicate x start position of all explosion particles
                $explosion_coordinate[$n][$o + 2] = $explosion_coordinate[0][$o + 2] ;duplicate y start position of all explosion particles
                $explosion_coordinate[$n][$o + 3] = _Random(-7, 7, 1) ;create random x vector (explosion particle speed)
                $explosion_coordinate[$n][$o + 4] = _Random(-7, 7, 1) ;create random y vector (explosion particle speed)
                $explosion_coordinate[$n][$o + 5] = Abs($explosion_coordinate[$n][3 + $o]) + Abs($explosion_coordinate[$n][4 + $o]) ;add absolute distance of vectors x and y
            Next
            ExitLoop
        EndIf
    Next
EndFunc   ;==>Explosion_Init

Func Explosion() ;draw explosions coordinates
    Local $q, $k
    $explosion_amount = 0
    For $k = 0 To $explosion_step * ($explosion_max_amount - 1) Step $explosion_step
        If $explosion_coordinate[0][$k] = 1 Then ;only draw active explosions
            $explosion_amount += 1
            For $q = 0 To $explosion_max_particle - 1
                $explosion_coordinate[$q][$k + 1] += $explosion_coordinate[$q][$k + 3] ;draw new x coordinate of a particle
                $explosion_coordinate[$q][$k + 2] += $explosion_coordinate[$q][$k + 4] ;draw new y coordinate of a particle
                $explosion_coordinate[$q][$k + 5] += Abs($explosion_coordinate[$q][$k + 3]) + Abs($explosion_coordinate[$q][$k + 4])
                If $explosion_coordinate[$q][$k + 5] <= $explosion_length Then ;draw until max. distance has been reached
                    _GDIPlus_GraphicsDrawEllipse($backbuffer, $explosion_coordinate[$q][$k + 1], $explosion_coordinate[$q][$k + 2], 2, 2, $pen)
                Else ;when max. distance has been reached then set x vector and y vector to 0
                    $explosion_coordinate[0][$k] = 0
                EndIf
            Next
        EndIf
    Next
    ConsoleWrite($explosion_amount & @CRLF)
EndFunc   ;==>Explosion

Func _Random($w1, $w2, $w3 = 0) ;just to avoid 0 as random number
Local $x = 0, $l1 = 0.50
While $x = 0
    $x = Random($w1, $w2, $w3)
    If $x < $l1 And $x >= 0 Then $x += $l1
    If $x > -$l1 And $x <= 0 Then $x -= $l1
WEnd
Return $x
EndFunc   ;==>_Random

If I'm using _GDIPlus_GraphicsClear($backbuffer, 0xFF000000) is running as expected!

Further, if it is extremly slow and you click (lmb) and hold it pressed on another window then it is running properly until you release the lmb again!

Is this a bug?

I discovered it coincidentally on my desktop pc (WinXP SP3). It is working properly on my both notebooks (Vista SP2 x32)!

I cannot see the reason why it is working properly on some machines and on the others not!

Thanks,

UEZ

Edited by UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Share this post


Link to post
Share on other sites
cppman

First of all, GDI+ is not hardware accelerated, so everything is done on the CPU. I'm going to make an assumption here and say that it is slow simply because when you have the alpha channel below 255, you're "enabling" alpha blending. Since this isn't hardware accelerated, the CPU has to go through each pixel and perform an equation (on each pixel) to get the right color.

Edit: try running it on a Vista+ desktop. This should also run just fine. Vista+ is based on Direct3D, so GDI+ will indirectly be hardware accelerated.

Edit: just tested it on a Vista desktop and it works just fine. However, on XP or lower it is very slow - so, my theory stands. :)

Edited by cppman

Share this post


Link to post
Share on other sites
UEZ

First of all, GDI+ is not hardware accelerated, so everything is done on the CPU. I'm going to make an assumption here and say that it is slow simply because when you have the alpha channel below 255, you're "enabling" alpha blending. Since this isn't hardware accelerated, the CPU has to go through each pixel and perform an equation (on each pixel) to get the right color.

Edit: try running it on a Vista+ desktop. This should also run just fine. Vista+ is based on Direct3D, so GDI+ will indirectly be hardware accelerated.

Edit: just tested it on a Vista desktop and it works just fine. However, on XP or lower it is very slow - so, my theory stands. :)

Why it is working properly on WinXP when you click and hold lmb on a different window?

Maybe it is a problem with gdi+ dll on WinXP!?!

UEZ

Edited by UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Share this post


Link to post
Share on other sites
cppman

Why it is working properly on WinXP when you click and hold lmb on a different window?

Maybe it is a problem with gdi+ dll on WinXP!?!

UEZ

That's odd. Maybe it's something about how Windows works prior to Vista. Other than that, I have no clue.

Share this post


Link to post
Share on other sites
UEZ

It seems to be a problem with _GDIPlus_GraphicsDrawImageRect() when alpha channel is < 0xff on WinXP (in a vm it worked properly!) because when I use this method it is working properly!

#include <GuiConstantsEx.au3>
;Coded by UEZ 2009
#AutoIt3Wrapper_Run_Obfuscator=y
#Obfuscator_Parameters=/sf /sv /om /cs=0 /cn=0
#AutoIt3Wrapper_Res_SaveSource=n
#AutoIt3Wrapper_UseUpx=n
;~ #AutoIt3Wrapper_Run_After=upx.exe --best "%out%"
#AutoIt3Wrapper_Run_After=upx.exe --ultra-brute "%out%"
#AutoIt3Wrapper_Run_After=del /f /q "Particle Explosions (from AUTOITEROIDS) old_Obfuscated.au3"

#include <GuiConstantsEx.au3>
#include <GDIPlus.au3>
#include <Array.au3>

Opt('MustDeclareVars', 1)
Opt("GUIOnEventMode", 1)

Global $fps = 0, $fps_diff, $fps_maintimer, $fps_timer
Global $hWnd, $hGraphic, $width, $height, $win_title = "GDI+: Particle Explosions (from AUTOITEROIDS modified) by UEZ 2009!"
$width = @DesktopWidth * 0.75
$height = @DesktopHeight * 0.75
Global Const $explosion_step = 8, $explosion_length = ($width + $height) / 7, $explosion_max_amount = 20, $explosion_max_particle = 15
Global $explosion_coordinate[$explosion_max_particle][$explosion_step * $explosion_max_amount] ; on/off, x, y, vx, vy, v, brush, color
Global $explosion_amount = 0
; Create GUI
$hWnd = GUICreate($win_title, $width, $height)
WinSetTrans($hWnd,"", 255); workaround for WinXP machines
_GDIPlus_Startup()
Global $graphics = _GDIPlus_GraphicsCreateFromHWND($hwnd)
Global $bitmap = _GDIPlus_BitmapCreateFromGraphics($width, $height, $graphics)
Global $backbuffer = _GDIPlus_ImageGetGraphicsContext($bitmap)
_GDIPlus_GraphicsClear($backbuffer)
_GDIPlus_GraphicsSetSmoothingMode($backbuffer, 2)
;~ Global $pen_size = 1
;~ Global $pen_color = 0xAFFF8070
;~ Global $pen = _GDIPlus_PenCreate($pen_color, $pen_size)
Global $brush_color
Global $x_min = $explosion_length * 0.75, $x_max = $width - $x_min, $y_min = $explosion_length * 0.75, $y_max = $height - $y_min

For $o = 0 To $explosion_step * ($explosion_max_amount - 1) Step $explosion_step
    $brush_color = 0; 0xFF000000 + Random(0x400000, 0xFFFFFF, 1)
    For $n = 0 To $explosion_max_particle - 1
;~      $brush_color = 0; 0xFF000000 + Random(0x400000, 0xFFFFFF, 1)
        $explosion_coordinate[$n][$o + 6] = _GDIPlus_BrushCreateSolid($brush_color)
    Next
Next
GUISetState()

GUISetOnEvent($GUI_EVENT_CLOSE, "Close")

$fps_maintimer = TimerInit()
Do
    $fps_timer = TimerInit()
    _GDIPlus_GraphicsClear($backbuffer, 0xA0000000)
    If Mod(Random(1, 10, 1), 9) >= 4 Then
    Explosion_Init(Random($x_min, $x_max, 1), Random($y_min, $y_max, 1))
    EndIf
    Explosion()
    _GDIPlus_GraphicsDrawImageRect($graphics, $bitmap, 0, 0, $width, $height)

    $fps_diff = TimerDiff($fps_timer)
    If TimerDiff($fps_maintimer) > 1000 Then
        $fps = Round(1000 / $fps_diff, 2)
        $fps_maintimer = TimerInit()
    EndIf
    WinSetTitle($hwnd, "", $win_title & " (fps: " & $fps & @TAB & ", #explosions: " & $explosion_amount & @TAB & ", #particles: " & $explosion_amount * $explosion_max_particle & ")")
Until Not Sleep(20)

Func Close()
    ; Clean up resources
    For $o = 0 To $explosion_step * ($explosion_max_amount - 1) Step $explosion_step
        For $n = 0 To $explosion_max_particle - 1
            _GDIPlus_BrushDispose($explosion_coordinate[$n][$o + 6])
        Next
    Next
    ;~ _GDIPlus_PenDispose($pen)
    _GDIPlus_BitmapDispose($bitmap)
    _GDIPlus_GraphicsDispose($graphics)
    _GDIPlus_GraphicsDispose($backbuffer)
    _GDIPlus_Shutdown()
    WinClose($hwnd)
    Exit
EndFunc


Func Explosion_Init($ex, $ey) ;initialise explosion
    For $o = 0 To $explosion_step * ($explosion_max_amount - 1) Step $explosion_step ;fill array with coordinate of hit object
        If $explosion_coordinate[0][$o] <> 1 Then
            $explosion_coordinate[0][$o] = 1
            $explosion_coordinate[0][$o + 1] = $ex ;save x coordinate
            $explosion_coordinate[0][$o + 2] = $ey ;save y coordinate
            $brush_color = 0xFF000000 + Random(0xF0F0F0, 0xFFFFFF, 1)
            For $n = 0 To $explosion_max_particle - 1
                $explosion_coordinate[$n][$o + 1] = $explosion_coordinate[0][$o + 1] ;duplicate x start position of all explosion particles
                $explosion_coordinate[$n][$o + 2] = $explosion_coordinate[0][$o + 2] ;duplicate y start position of all explosion particles
                $explosion_coordinate[$n][$o + 3] = _Random(-7, 7, 1) ;create random x vector (explosion particle speed)
                $explosion_coordinate[$n][$o + 4] = _Random(-7, 7, 1) ;create random y vector (explosion particle speed)
                $explosion_coordinate[$n][$o + 5] = Abs($explosion_coordinate[$n][3 + $o]) + Abs($explosion_coordinate[$n][4 + $o]) ;add absolute distance of vectors x and y
;~              $brush_color = 0xFF000000 + Random(0xF0F0F0, 0xFFFFFF, 1) ;each particle gets another color
                $explosion_coordinate[$n][$o + 7] = $brush_color
                _GDIPlus_BrushSetSolidColor($explosion_coordinate[$n][$o + 6], $explosion_coordinate[$n][$o + 7])
            Next
            ExitLoop
        EndIf
    Next
EndFunc ;==>Explosion_Init

Func Explosion() ;draw explosions coordinates
    Local $q, $k, $r, $g, $b
    $explosion_amount = 0
    For $k = 0 To $explosion_step * ($explosion_max_amount - 1) Step $explosion_step
    If $explosion_coordinate[0][$k] = 1 Then ;only draw active explosions
            $explosion_amount += 1
    For $q = 0 To $explosion_max_particle - 1
    $explosion_coordinate[$q][$k + 1] += $explosion_coordinate[$q][$k + 3] ;draw new x coordinate of a particle
    $explosion_coordinate[$q][$k + 2] += $explosion_coordinate[$q][$k + 4] ;draw new y coordinate of a particle
    $explosion_coordinate[$q][$k + 5] += Abs($explosion_coordinate[$q][$k + 3]) + Abs($explosion_coordinate[$q][$k + 4]) ;absolute vector length
    If $explosion_coordinate[$q][$k + 5] <= $explosion_length Then ;draw until max. distance has been reached
;~  _GDIPlus_GraphicsDrawEllipse($backbuffer, $explosion_coordinate[$q][$k + 1], $explosion_coordinate[$q][$k + 2], 2, 2, $pen)
                    _GDIPlus_GraphicsFillEllipse($backbuffer, $explosion_coordinate[$q][$k + 1], $explosion_coordinate[$q][$k + 2], 3, 3, $explosion_coordinate[$q][$k + 6])
                    ;fade out colors
                    $r = BitAND($explosion_coordinate[$q][$k + 7], 0x00FF0000) / 0x10000 * 0.91 ;decrease red channel
                    $g = BitAND($explosion_coordinate[$q][$k + 7], 0x0000FF00) / 0x100 * 0.91 ;decrease green channel
                    $b = BitAND($explosion_coordinate[$q][$k + 7], 0x000000FF) * 0.91 ;decrease blue channel
                    $explosion_coordinate[$q][$k + 7] = "0xFF" & Hex($r, 2) & Hex($g, 2) & Hex($b, 2)
                    _GDIPlus_BrushSetSolidColor($explosion_coordinate[$q][$k + 6], $explosion_coordinate[$q][$k + 7]) ;set new color
    Else ;when max. distance has been reached then set x vector and y vector to 0
                    $explosion_coordinate[0][$k] = 0
    EndIf
    Next
    EndIf
    Next
EndFunc ;==>Explosion

Func _Random($w1, $w2, $w3 = 0) ;just to avoid 0 as random number
    Local $x = 0, $l1 = 0.50
    While $x = 0
        $x = Random($w1, $w2, $w3)
        If $x < $l1 And $x >= 0 Then $x += $l1
        If $x > -$l1 And $x <= 0 Then $x -= $l1
    WEnd
    Return $x
EndFunc ;==>_Random

I have no idea why! :)

UEZ

Edit: seems to be working because value for alpha channel in function _GDIPlus_GraphicsClear() is not working or I made a mistake ;)

Edit2: a workaround seems to be to use WinSetTrans(). Thanks to eukalyptus for the hint :)

Edited by UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×