#include <opencl_easy.au3>
#include <GUIConstantsEx.au3>
#include <WinAPI.au3>
#include <GDIConstants.au3>



$hgui = GUICreate("OpenCL Devices.....please wait", 1024, 200)
GUISetState()

$listview = GUICtrlCreateListView("Platform|Device Nr.|ID|Type|Name Device|Compute Units|Mhz|Workgroupsize|Speed (higher=better)", -1, -1, 1024, 200)

Global $KernelSource = "__kernel void Mandelbrot( __global int* output, const unsigned int width,const unsigned int height,const float centerx,const float centery,const uint maxiter,const float apfelbreite)" & @CRLF & _
        "{" & @CRLF & _
        "uint iter;" & @CRLF & _                            ;lokale variablen
        "float xtemp;" & @CRLF & _
        "uint threadid = get_global_id(0);" & @CRLF & _         ;thread-id, d.h. jedes pixel von 1 bis b*h
        "float py       = threadid/height;" & @CRLF & _           ;pixelkoordinaten
        "float px       = threadid%width;" & @CRLF & _           ;pixelkoordinaten
        "float x0      = (centerx-apfelbreite*0.5f)+px*apfelbreite/width;" & @CRLF & _      ;...Berechnungen
        "float y0      = (centery-apfelbreite*0.5f)+py*apfelbreite/height;" & @CRLF & _      ;...Berechnungen
        "float x      = x0;" & @CRLF & _
        "float y      = y0;" & @CRLF & _
        "for (iter=0;iter<maxiter;iter++)" & @CRLF & _      ;...Berechnungen
        "{" & @CRLF & _                                     ;...Berechnungen
        "xtemp      = x*x -y*y + x0;" & @CRLF & _
        "y             = 2 * x * y + y0;" & @CRLF & _
        "x             = xtemp;" & @CRLF & _
        "if ((x * x + y * y) > 4) break;" & @CRLF & _
        "}" & @CRLF & _                                     ;...Berechnungen
        "output[threadid]  = 0x0008002* native_log2(native_log2((float) iter+1));" & @CRLF & _ ;Pixel schreiben 0xFFBBGGRR, BB=GG=RR=t    native_powr(iter,2)
        "}"

Local $num_device = 1
Local $gpu = 1                                              ;
Local $numDevices                                           ;anzahl der Geräte
Local $device_id, $devices                                  ;Geräte ID
Local $num_platforms = 0                                    ; _dllstructcreate16("dword");struct



;zum Ermitteln der Geschwindigkeit des Devices wird ein Ausschnitt der Mandelbrotmenge berechnet
$width = 1500
$height = 550

Global $DATA_SIZE = $width * $height                        ;bildgröße=anzahl pixel (rgba)

Local $ptr_bitmap, $hbmp_bitmap                             ;byref
$hDC_bitmap = _CreateNewBmp32($width, $height, $ptr_bitmap, $hbmp_bitmap) ;DC, Pointer auf die Bitmapdaten und ein Handle für GDI+....eine eierlegende Wollmilchsau


;Anzahl platforms bestimmen**********
clGetPlatformIDs(10, $platforms, $num_platforms)
ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $num_platforms = ' & $num_platforms & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
If $num_platforms <= 0 Then
    MsgBox(0, "AutoIt OpenCl", "Sorry, no Device for OpenCl detected. Please install the newest driver or/and the SDK from the vendor of your devices!")
    Exit
EndIf
Global $platform[$num_platforms + 1]
;~  $platform = DllStructGetData($platforms, 1)

;~ $param_name=0x00900
;~ $param_value_size=200
;~  $para_struct = _dllstructcreate16("char[" & $param_value_size & "]") ;platz für vendor-string
;~ ; $aCall = DllCall($OpenCl_dll, "dword", "clGetDeviceInfo"  , "dword", $deviceid, "dword", $param_name, "dword", $param_value_size, "dword", $param_value,                  "dword", DllStructGetPtr($param_value_size_ret))
;~  $aCall = DllCall($OpenCl_dll, "dword", "clGetPlatformInfo", "dword", $platform,         "dword", $param_name, "dword", $param_value_size, "dword", dllstructgetptr($para_struct), "dword", DllStructGetPtr($param_value_size_ret))
;~  ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $aCall = ' & $aCall & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console
;~     $info_device = DllStructGetData($para_struct, 1)
;~     ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $info_device = ' & $info_device & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console
;~ ;_arraydisplay($acall)
;~ exit
For $i = 1 To $num_platforms
    ;MsgBox(262144,'Debug line ~' & @ScriptLineNumber,'Selection:' & @lf & '$i' & @lf & @lf & 'Return:' & @lf & $i) ;### Debug MSGBOX
    $platform[$i] = DllStructGetData($platforms, $i)
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $platform[$i] = ' & $platform[$i] & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console



    ;context erstellen, mit welchem Gerät wird gearbeitet (CPU oder Grafikkarte)**********
    $context_properties = _dllstructcreate16("dword[5]")    ;context-Eigenschaften
    DllStructSetData($context_properties, 1, $CL_CONTEXT_PLATFORM, 1)
    DllStructSetData($context_properties, 1, $platform[1], 2) ;platform herraussuchen und damit die Berechnung durchführen!
    DllStructSetData($context_properties, 1, 0, 3)
    DllStructSetData($context_properties, 1, 0, 4)
    DllStructSetData($context_properties, 1, 0, 5)
    ; DllStructSetData($context_properties, 1, $CL_CONTEXT_OFFLINE_DEVICES_AMD, 3)          ;$CL_CONTEXT_OFFLINE_DEVICES_AMD erlaubt das verwenden von binary kernels auf allen platformen
    ; DllStructSetData($context_properties, 1, 1, 4);wenn $CL_CONTEXT_OFFLINE_DEVICES_AMD verwendet wird
    $pfn_notify = _dllstructcreate16("dword[5]")            ;keine callbackfunktion zum errorhandling der shaderfuncs
    $user_data = _dllstructcreate16("dword")
    ;context anhand des Gerätetyps erstellen
    $context = clCreateContextFromType($context_properties, $CL_DEVICE_TYPE_ALL, $pfn_notify, $user_data, $errcode_ret)
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $context = ' & $context & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console


    ;Informationen uber das device holen
    $param_name = $CL_CONTEXT_DEVICES
    $devicelistsize = clGetContextInfo($context, $param_name, 0, 0, $param_value_size_ret); anzahl devices
    $devicelistsize = DllStructGetData($param_value_size_ret, 1, 1)
    $devices = _dllstructcreate16("dword[" & $devicelistsize & "]") ;liste anlegen mit anzahl der devices
    $devicelistsize = clGetContextInfo($context, $param_name, $devicelistsize, DllStructGetPtr($devices), $param_value_size_ret);einzelne devices auflisten
    $devicelistsize = DllStructGetData($param_value_size_ret, 1, 1)
    $devicecount = $devicelistsize / 4

    ;Device (Gerät) auswählen

    For $num_device = 1 To $devicecount                     ;sämtliche devices anzeigen
        $param_name = $CL_DEVICE_NAME                       ;gesucht ist der Gerätename
        $param_value_size = 200                             ;zeichenstring für Gerätenamen
        $para_struct = _dllstructcreate16("char[" & $param_value_size & "]") ;platz für vendor-string
        ;    msgbox(0,"start",0)
        $deviceid = DllStructGetData($devices, 1, $num_device) ;das Gerät aus dieser Liste wird verwendet
        ;Gerätenamen anzeigen lassen
        $device_info = clGetDeviceInfo($deviceid, $param_name, $param_value_size, DllStructGetPtr($para_struct), $param_value_size_ret)
        $info_device = DllStructGetData($para_struct, 1)    ;das ist die rückgabe der clGetDeviceInfo()-Funktion!
        ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $numberdevice = ' & $num_device & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
        ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $namedevice = ' & $info_device & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
        ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $deviceid = ' & $deviceid & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console

        ;      If $num_device < 1 Or $num_device > $devicecount Then $num_device = 1
        $deviceid = DllStructGetData($devices, 1, $num_device) ;das in der Funktion übergebene Gerät wird verwendet
        ConsoleWrite("verwendetes Device Nr. " & $num_device & @CRLF)
        ;commandqueue erstellen
        ConsoleWrite("clCreateCommandQueue" & @CRLF)
        $properties = 0
        $queue_prop = _dllstructcreate16("int64[5]")
        DllStructSetData($queue_prop, 1, 0, 1)
        DllStructSetData($queue_prop, 1, 0, 2)              ;platform herraussuchen und damit die Berechnung durchführen!
        DllStructSetData($queue_prop, 1, 0, 3)              ;$CL_CONTEXT_OFFLINE_DEVICES_AMD)
        DllStructSetData($queue_prop, 1, 1, 4)
        DllStructSetData($queue_prop, 1, 0, 5)

;~         $devices_cl = _dllstructcreate16("dword[5]")
;~         DllStructSetData($devices_cl, 1, $deviceid)

        ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $context = ' & $context & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
        ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $deviceid = ' & $deviceid & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console

        $command_queue = clCreateCommandQueue($context, $deviceid, $properties, $errcode_ret)
        ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $commandqueue = ' & $command_queue & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console


        ;programm aus kernelsource erstellen (integrierter compiler)
        $program = clCreateProgramWithSource($context, 1, $KernelSource, StringLen($KernelSource), $errcode_ret)
        ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $program = ' & $program & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console

        ;programm vorkompilieren
        clBuildProgram($program, $num_device, DllStructGetPtr($devices), 0, 0, 0)

        ;info des Compilers, es wird ein Log erstellt mit Fehlermeldungen
        ;ConsoleWrite("clGetProgramBuildInfo LOG" & @CRLF)
        $param_value = 0                                    ;_DllStructCreate16("uint")                                               ; rückgabe in $param_value_size_ret ist die Größe des log´s in char
        $param_value_size = 0
        $param_name = $CL_PROGRAM_BUILD_LOG                 ;log des compilers aufrufen


        clGetProgramBuildInfo($program, $deviceid, $param_name, $param_value_size, $param_value, $param_value_size_ret)
        $logsize = DllStructGetData($param_value_size_ret, 1) ;größe des logs
        $slog = _dllstructcreate16("char[" & $logsize & "]") ;inhalt log
        clGetProgramBuildInfo($program, $deviceid, $param_name, $logsize, DllStructGetPtr($slog), $param_value_size_ret)
        $logsize = DllStructGetData($param_value_size_ret, 1) ;größe des logs
        $log = DllStructGetData($slog, 1)
        If StringLen($log) > 1 Then ConsoleWrite("LOG: " & @CRLF & $log & @CRLF)


        ;Kernel erstellen für jedes einzelne workitem
        ;kernelname extrahieren, der Kernelname steht idR vor der öffnenden Klammer, Regex needed^^
        $kname1 = StringInStr($KernelSource, "__kernel")
        $kname2 = StringInStr($KernelSource, "(", 0, 1, $kname1)
        $kname = StringSplit(StringStripWS(StringMid($KernelSource, $kname1, $kname2 - $kname1), 6), " ", 3)
        $kernel = clCreateKernel($program, $kname[UBound($kname) - 1], $errcode_ret)

        ;Anzahl der Workgroups bestimmen (nur zur Info...)
        $param_name = $CL_DEVICE_MAX_WORK_GROUP_SIZE        ; Gesucht ist die maximale Anzahl Workgroups, von denen die Kernel abgearbeitet werden
        $param_value_size = 4                               ;größe des parameters in byte (uint=4 byte)
        $para_struct = _dllstructcreate16("uint[" & $param_value_size & "]") ;jaja...  :)...Speicherverschwender
        $device_info = clGetDeviceInfo($deviceid, $param_name, $param_value_size, DllStructGetPtr($para_struct), $param_value_size_ret)
        $MAX_WORK_GROUP_SIZE = DllStructGetData($para_struct, 1) ;ergebnis aus der struct auslesen

        ;Anzahl der Compute-Units bestimmen (nur zur Info...)
        $param_name = $CL_DEVICE_MAX_compute_units          ; Gerätename anzeigen lassen
        $param_value_size = 4                               ;größe des parameters in byte (uint=4 byte)
        $para_struct = _dllstructcreate16("uint[" & $param_value_size & "]") ;jaja...  :)...Speicherverschwender
        $device_info = clGetDeviceInfo($deviceid, $param_name, $param_value_size, DllStructGetPtr($para_struct), $param_value_size_ret)
        $MAX_compute_units = DllStructGetData($para_struct, 1) ;ergebnis aus der struct auslesen
        ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $MAX_compute_units = ' & $MAX_compute_units & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console

        ;Typ (CPU/GPU) bestimmen (nur zur Info...)
        $param_name = $CL_DEVICE_TYPE                       ; Gerätename anzeigen lassen
        $param_value_size = 8                               ;größe des parameters in byte (uint=4 byte)
        $para_struct = _dllstructcreate16("uint[" & $param_value_size & "]") ;jaja...  :)...Speicherverschwender
        $device_info = clGetDeviceInfo($deviceid, $param_name, $param_value_size, DllStructGetPtr($para_struct), $param_value_size_ret)
        $devicetyp = DllStructGetData($para_struct, 1)      ;ergebnis aus der struct auslesen

        ;Takt in Mhz (CPU/GPU) bestimmen (nur zur Info...)
        $param_name = $CL_DEVICE_MAX_CLOCK_FREQUENCY        ; Gerätename anzeigen lassen
        $param_value_size = 8                               ;größe des parameters in byte (uint=4 byte)
        $para_struct = _dllstructcreate16("uint[" & $param_value_size & "]") ;jaja...  :)...Speicherverschwender
        $device_info = clGetDeviceInfo($deviceid, $param_name, $param_value_size, DllStructGetPtr($para_struct), $param_value_size_ret)
        $mhz = DllStructGetData($para_struct, 1)            ;ergebnis aus der struct auslesen

;~         ;WorkItemSize bestimmen (nur zur Info...)
;~         $param_name = $CL_DEVICE_Vendor_id                      ; Gerätename anzeigen lassen
;~         $param_value_size = 4                      ;größe des parameters in byte (uint=4 byte)
;~         $para_struct = _dllstructcreate16("char[" & $param_value_size & "]") ;jaja...  :)...Speicherverschwender
;~         $device_info = clGetDeviceInfo($deviceid, $param_name, $param_value_size, DllStructGetPtr($para_struct), $param_value_size_ret)
;~         $deviceworkitemsize = DllStructGetData($para_struct, 1)      ;ergebnis aus der struct auslesen
;~         for $f=1 to 10
;~             $a=asc(stringmid($deviceworkitemsize,$f,1))
;~             ConsoleWrite('@@ Debug(' & $f & ') : $a = ' & $a &  @crlf) ;### Debug Console
;~         next
;~         MsgBox(262144,'Debug line ~' & @ScriptLineNumber,'Selection:' & @lf & '$deviceworkitemsize' & @lf & @lf & 'Return:' & @lf & $deviceworkitemsize) ;### Debug MSGBOX



        Switch $devicetyp
            Case 2
                $typ = "CPU"
            Case 4
                $typ = "GPU"
            Case Else
                $typ = $devicetyp & " unnknown"
        EndSwitch
        ;$listview = GUICtrlCreateListView("Platform|Device Nr.|ID|Name Device|Compute Units|Workgroupsize|Speed (higher=better)", -1, -1, 1024, 200)

        For $param_name = 0x01031 To 0x01031                ;2b
            ; Info anzeigen lassen
            $param_value_size = 400                         ;größe des parameters in byte (uint=4 byte)
            $para_struct = _dllstructcreate16("char[" & $param_value_size & "]") ;jaja...  :)...Speicherverschwender
            $device_info = clGetDeviceInfo($deviceid, $param_name, $param_value_size, DllStructGetPtr($para_struct), $param_value_size_ret)
            $info = DllStructGetData($para_struct, 1)       ;ergebnis aus der struct auslesen
            ;     MsgBox(262144, 'Debug line ~' & @ScriptLineNumber, 'Selection:' & @LF & '$info' & @LF & @LF & 'Return:' & @LF & $info) ;### Debug MSGBOX
        Next

        $speed = speedtest($num_device)

        GUICtrlCreateListViewItem($platform[$i] & "|" & $num_device & "|" & $deviceid & "|" & $typ & "|" & $info_device & "|" & $MAX_compute_units & "|" & $mhz & "|" & $MAX_WORK_GROUP_SIZE & "|" & $speed, $listview)
    Next
Next
WinSetTitle($hgui,"","OpenCL Devices")


While GUIGetMsg() <> -3
WEnd
Exit
;==>



Func speedtest($devnr)

    ConsoleWrite("SPEEDTEST" & @CRLF)

    $output_buffer = DllStructCreate("dword[" & $DATA_SIZE & "]", $ptr_bitmap);halleluja! bitmaps werden immer 16byte-aligned!
    $CL_buffer_out = _CL_CreateBuffer($output_buffer)

    Global $NULL = 0

    ;mandelbrot parameter
    Global $maxiteration = 280                              ;maximale iterationen
    Global $centerx = -0.905043734434198                    ;mitte Bild in x-richtung
    Global $centery = 0.251232073131859                     ;mitte bild in y-richtung
    Global $apfelbreite = 0.0155929924241542                ;breite des Bildinhalts x-richtung



    ;Parameter an den Kernel übergeben
    _CL_SetArg(0, "ptr*", $CL_buffer_out)
    _CL_SetArg(1, "uint*", $width)
    _CL_SetArg(2, "uint*", $height)
    _CL_SetArg(3, "float*", $centerx)
    _CL_SetArg(4, "float*", $centery)
    _CL_SetArg(5, "uint*", $maxiteration)
    _CL_SetArg(6, "float*", $apfelbreite)


    $CL_DEBUGFLAG = 0                                       ;Debug-Ausgabe ausschalten, um Geschwindigkeit zu erhöhen

    $t = TimerInit()
    Global $fps = 0

    While TimerDiff($t) < 2000                              ;2 sek

        _CL_SetArg(3, "float*", $centerx)
        _CL_SetArg(4, "float*", $centery)
        _CL_SetArg(5, "uint*", $maxiteration)
        _CL_SetArg(6, "float*", $apfelbreite)
        _CL_RunKernel($DATA_SIZE, 0)                        ;Kernel ausführen,
        _CL_ReadBuffer($CL_buffer_out, $output_buffer)      ;Puffer(alle Pixel) lesen
        ;   _WinAPI_BitBlt($hdc_gui, 0, 0, $width, $height, $hDC_bitmap, 0, 0, $srccopy) ;Bitmap in die GUI blitten
        $fps += 1                                           ;Frames pro Sekunde zählen
    WEnd

    ;...das wars schon^^
    Return $fps
EndFunc                                                     ;==>speedtest


Func _CreateNewBmp32($iwidth, $iheight, ByRef $ptr, ByRef $hbmp) ;erstellt leere 32-bit-Bitmap; Rückgabe DC und ptr und handle auf die Bitmapdaten
    ;by Andy
    Local $hcdc = _WinAPI_CreateCompatibleDC(0)             ;Desktop-Kompatiblen DeviceContext erstellen lassen
    Local $tBMI = DllStructCreate($tagBITMAPINFO)           ;Struktur der Bitmapinfo erstellen und Daten eintragen
    DllStructSetData($tBMI, "Size", DllStructGetSize($tBMI) - 4) ;Structgröße abzüglich der Daten für die Palette
    DllStructSetData($tBMI, "Width", $iwidth)
    DllStructSetData($tBMI, "Height", -$iheight)            ;minus =standard = bottomup
    DllStructSetData($tBMI, "Planes", 1)
    DllStructSetData($tBMI, "BitCount", 32)                 ;32 Bit = 4 Bytes => AABBGGRR
    Local $adib = DllCall('gdi32.dll', 'ptr', 'CreateDIBSection', 'hwnd', 0, 'ptr', DllStructGetPtr($tBMI), 'uint', $DIB_RGB_COLORS, 'ptr*', 0, 'ptr', 0, 'uint', 0)
    $hbmp = $adib[0]                                        ;hbitmap handle auf die Bitmap, auch per GDI+ zu verwenden
    $ptr = $adib[4]                                         ;pointer auf den Anfang der Bitmapdaten, vom Assembler verwendet
    _WinAPI_SelectObject($hcdc, $hbmp)                      ;objekt hbitmap in DC
    Return $hcdc                                            ;DC der Bitmap zurückgeben
EndFunc                                                     ;==>_CreateNewBmp32










