#AutoIt3Wrapper_usex64=n
;8. November 2011
#include <opencl_inc.au3>
;Funktionen zum Vereinfachen der API-Calls


Global $KernelSource, $context, $deviceid, $command_queue, $program, $kernel
Global $input_buffer_size, $output_buffer_size
Global $num_events_in_wait_list = 0, $event_wait_list = 0, $eventlist = _dllstructcreate16("dword[5]");struct



Func _cl_GetDevice($device = "CPU", $num_device = 1)                  ;nummer des device, welches verwendet werden soll
    Local $gpu = 1                                                    ;
    Local $numDevices                                                 ;anzahl der Geräte
    Local $device_id, $devices                                        ;Geräte ID
    Local $num_platforms = 0                                          ; _dllstructcreate16("dword");struct

    Select
        Case $device = "GPU"
            $GERAET = $CL_DEVICE_TYPE_GPU
        Case $device = "CPU"
            $GERAET = $CL_DEVICE_TYPE_CPU
        Case Else
            $GERAET = $CL_DEVICE_TYPE_ALL
    EndSelect
    ;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 istall the newest driver or/and the SDK from the vendor of your devices!")
        Exit
    EndIf
    Global $platform[$num_platforms + 1]
    For $i = 1 To $num_platforms
        $platform[$i] = DllStructGetData($platforms, $i)
        ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $platform[$i] = ' & $platform[$i] & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    Next


    ;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, $GERAET, $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
    ;    MsgBox(262144,'Debug line ~' & @ScriptLineNumber,'Selection:' & @lf & '$devicelistsize' & @lf & @lf & 'Return:' & @lf & $devicelistsize) ;### Debug MSGBOX
    $devicelistsize = DllStructGetData($param_value_size_ret, 1, 1)
    ;    MsgBox(262144,'Debug line ~' & @ScriptLineNumber,'Selection:' & @lf & '$devicelistsize' & @lf & @lf & 'Return:' & @lf & $devicelistsize) ;### Debug MSGBOX
    $devicecount = $devicelistsize / 4

    ;hier device (Gerät) auswählen
    $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
    For $numberdevice = 1 To $devicecount                             ;sämtliche devices anzeigen
        $deviceid = DllStructGetData($devices, 1, $numberdevice)      ;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)
        $infodevice = DllStructGetData($para_struct, 1)               ;das ist die rückgabe der clGetDeviceInfo()-Funktion!
        ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $numberdevice = ' & $numberdevice & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
        ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $namedevice = ' & $infodevice & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
        ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $deviceid = ' & $deviceid & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    Next

    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

    $aCall = DllCall($OpenCl_dll, "dword", "clCreateCommandQueue", "uint", $context, "dword", $deviceid, "int64", 0, "int*", 0);DllStructGetPtr($queue_prop),

    ;_arraydisplay($acall)
    $command_queue = $aCall[0]
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $commandqueue = ' & $command_queue & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    $error = OpenCL_DecodeError($aCall[4])
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $error = ' & $error & @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
    ;$num_devices = 1
    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
EndFunc                                                               ;==>_cl_GetDevice


Func _CL_CreateBuffer($buffer)
    $flags = BitOR($CL_MEM_READ_WRITE, $CL_MEM_use_HOST_PTR)
    $buffer_size = DllStructGetSize($buffer)
    $host_ptr = DllStructGetPtr($buffer)
    Return clCreateBuffer($context, $flags, $buffer_size, $host_ptr, $errcode_ret)
EndFunc                                                               ;==>_CL_CreateBuffer

Func _CL_WriteBuffer($CL_buffer, $write_buffer)
    Return clEnqueueWriteBuffer($command_queue, $CL_buffer, $CL_true, 0, DllStructGetSize($write_buffer), DllStructGetPtr($write_buffer), $num_events_in_wait_list, $event_wait_list, $eventlist)
EndFunc                                                               ;==>_CL_WriteBuffer

Func _CL_ReadBuffer($CL_buffer, $read_buffer)
    Return clEnqueueReadBuffer($command_queue, $CL_buffer, $CL_true, 0, DllStructGetSize($read_buffer), DllStructGetPtr($read_buffer), $num_events_in_wait_list, $event_wait_list, $eventlist)
EndFunc                                                               ;==>_CL_ReadBuffer

Func _CL_SetArg($arg_index, $arg_type, $arg_value)
    Return clSetKernelArg($kernel, $arg_index, 4, $arg_type, $arg_value)
EndFunc                                                               ;==>_CL_SetArg

Func _CL_RunKernel($global, $local)
    $globalDS = _dllstructcreate16("uint")
    DllStructSetData($globalDS, 1, $global)
    $localDS = _dllstructcreate16("uint")
    DllStructSetData($localDS, 1, $local)
    If $local <> 0 Then
        $localWS = DllStructGetPtr($localDS)
    Else
        $localWS = 0                                                  ;OpenCl ermittelt selbstständig eine Workgroupsize
    EndIf
    $work_dim = 1
    Return clEnqueueNDRangeKernel($command_queue, $kernel, $work_dim, 0, DllStructGetPtr($globalDS), $localWS, $num_events_in_wait_list, $event_wait_list, $eventlist)
EndFunc                                                               ;==>_CL_RunKernel










