Schnellste 2d Grafikengine

    • Offizieller Beitrag

    Heyho,

    Mal eine Frage. Habe mich lange nicht mehr mit dem Thema Grafik in AutoIt auseinandergesetzt, das letze mir bekannte ist GDI+ ;) Ich bräuchte eine möglichst schnelle Engine um ein 2d Grafen zu Zeichen für ein Oszilloskop. Schön währe ne Wiederholfrequenz von 30fps oder mehr, Auflösung sollte so an die 1280x1024 ran gehen. GDI+ wird dafür wohl zu langsam sein, oder? Was gibt es aktuell für Alternativen?

    So sollte es später ungefähr aussehen:
    autoit.de/wcf/attachment/14661/


    Vielen Dank,
    Gruß
    Spider

  • Hi,
    wie, bzw. in welcher Form erfolgt die Ausgabe des Oszilloskops?

    • Offizieller Beitrag

    Hallo Andy,

    Der Oszi hat (natürlich) einen internen Buffer, den ich dann sozusagen nach und nach via USB auslese. Aussehen werden die Daten wohl wie folgt, wobei ich mir noch nicht hundertprozentig sicher bin, ob diese Daten auch korrekt sind. Hier zum Beispiel habe ich mit dem eingebauten Generator eine Sinuskurve erstellt, aber die Werte die ich erhalte haben nichts mit einer Sinuskurve zu tun. Aber so bekomme ich bisher die Daten da raus (im Edit, getrennt durch Semikolon, in diesem Beispiel sind es nur 200 Samples)

    Gruß
    Spider

  • Moin!

    Also bei 2D kenne ich nur noch die QuickDraw und die Irrlicht - Man könnte auch vielleicht die OpenGL UDF nehmen. Auf jeden Fall hat die QuickDraw eine höhere Performance und auch eine bessere Bildqualität als die Irrlicht in 2D.

    Grüsse!

  • Ich halte GDI+ definitiv NICHT für zu langsam
    Und auch für die angenehmste, einfachste und umfangreichste Methode

    Zum Testen der Framerate, wenn nur eine Kurve gezeichnet wird:

    Spoiler anzeigen
    [autoit]

    #include <GDIPlus.au3>
    #include <GUIConstantsEx.au3>
    #include <WindowsConstants.au3>

    [/autoit] [autoit][/autoit] [autoit]

    Global $iWidth = 1280
    Global $iHeight = 1024

    [/autoit] [autoit][/autoit] [autoit]

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

    [/autoit] [autoit][/autoit] [autoit]

    Global $hGui = GUICreate("Sine", $iWidth, $iHeight, 0, 0, $WS_POPUP)
    GUISetOnEvent($GUI_EVENT_CLOSE, "_Exit")
    _GDIPlus_Startup()
    Global $hDC = _WinAPI_GetDC($hGui)
    Global $hBMP = _WinAPI_CreateCompatibleBitmap($hDC, $iWidth, $iHeight)
    Global $hBmpTmp = _GDIPlus_BitmapCreateFromHBITMAP($hBMP)
    _WinAPI_DeleteObject($hBMP)
    $hBMP = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBmpTmp)
    _GDIPlus_BitmapDispose($hBmpTmp)
    Global $hCDC = _WinAPI_CreateCompatibleDC($hDC)
    Global $hOBJ = _WinAPI_SelectObject($hCDC, $hBMP)
    Global $hGfxBuffer = _GDIPlus_GraphicsCreateFromHDC($hCDC)
    ;_GDIPlus_GraphicsSetSmoothingMode($hGfxBuffer, 2)
    _GDIPlus_GraphicsClear($hGfxBuffer, 0xFFFFFFFF)

    [/autoit] [autoit][/autoit] [autoit]

    Global $hPen = _GDIPlus_PenCreate(0xFF00FF00, 2)

    [/autoit] [autoit][/autoit] [autoit]

    GUIRegisterMsg($WM_PAINT, "WM_PAINT")
    GUIRegisterMsg($WM_ERASEBKGND, "WM_PAINT")
    GUISetState()

    [/autoit] [autoit][/autoit] [autoit]

    Global $iPnt = 20
    Global $tOsc = DllStructCreate("float[" & $iPnt * 2 & "];")
    Global $pOsc = DllStructGetPtr($tOsc)
    For $i = 0 To $iPnt - 1
    DllStructSetData($tOsc, 1, $i * $iWidth / ($iPnt - 1), $i * 2 + 1)
    Next

    [/autoit] [autoit][/autoit] [autoit]

    Global $iFrms = 0
    Global $iTimer = TimerInit()
    While 1
    $iFrms += 1
    If Mod($iFrms, 10) = 0 Then ToolTip(Round($iFrms / (TimerDiff($iTimer) / 1000), 2) & "fps")
    _Draw()
    ;Sleep(10)
    WEnd

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _Draw()
    _GDIPlus_GraphicsClear($hGfxBuffer, 0xFFFFFFFF)

    [/autoit] [autoit][/autoit] [autoit]

    For $i = 0 To $iPnt
    DllStructSetData($tOsc, 1, Random(0, $iHeight), $i * 2 + 2)
    Next

    [/autoit] [autoit][/autoit] [autoit]

    DllCall($ghGDIPDll, "int", "GdipDrawCurve", "handle", $hGfxBuffer, "handle", $hPen, "ptr", $pOsc, "int", $iPnt)

    [/autoit] [autoit][/autoit] [autoit]

    _WinAPI_BitBlt($hDC, 0, 0, $iWidth, $iHeight, $hCDC, 0, 0, 0x00CC0020)
    EndFunc ;==>_Draw

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func WM_PAINT($hWnd, $uMsgm, $wParam, $lParam)
    _WinAPI_BitBlt($hDC, 0, 0, $iWidth, $iHeight, $hCDC, 0, 0, 0x00CC0020)
    Return $GUI_RUNDEFMSG
    EndFunc ;==>WM_PAINT

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _Exit()
    _GDIPlus_PenDispose($hPen)
    _GDIPlus_GraphicsDispose($hGfxBuffer)
    _WinAPI_SelectObject($hCDC, $hOBJ)
    _WinAPI_DeleteObject($hBMP)
    _WinAPI_DeleteDC($hCDC)
    _WinAPI_ReleaseDC($hGui, $hDC)
    _GDIPlus_Shutdown()
    Exit
    EndFunc ;==>_Exit

    [/autoit]

    E

  • Hi GtaSpider,
    man müsste die Daten einfach mal auswerten, ich vermute, daß Zeit/Amplitudenpaare angegeben sind.
    Die Frequenz sind 1000 Hertz, könnte sein, daß nicht die Daten (Amplituden) einer einzigen Schwingung, sondern fortlaufend alle x Milli(micro)sekunden im Verlauf ausgegeben werden.
    Probier mal 1 Hertz als Frequenz, als Ausgabe sollte dann die Sinuskurve erfolgen
    Kann auch sein, daß man durch 5 aufeinanderfolgende Punkte eine Funktion 5. Ordnung legen muß 8o . Das wäre dann aber nur Fleißarbeit :thumbup:


    Bezüglich Geschwindigkeit:
    Wie viele Datenpaare werden pro Sekunde maximal vom Oszilloskop geliefert?
    Gibt es eine API um direkt auf den Speicherbereich der ausgelesenen Daten zuzugreifen oder werden die in eine Datei geschrieben?

    Zitat

    Das schnellste ist denk ich Prospeed für AutoIt

    Prospeed nutzt die GDI-Funktionen zur Darstellung, lediglich die Filter/Spritebewegungen sind von Frank Abbing in Assembbler programmiert.
    Gehen wir mal von 1280 Pixeln in x-Richtung aus, 3 uberlagerte einzelne Frequenzen gibt über den dicken Daumen ca 4000 "Pixel", die pro Frame zu setzen sind.
    Bei gewünschten 30 FPS bleiben pro Frame 33 Millisekunden fürs Auslesen der Daten und Darstellung auf dem Bildschirm.
    Das sind 8,3 µ-Sekunden pro Pixel....da wird ein GDI+-"Setpixel()" schon Schwierigkeiten bekommen^^.
    AutoIt fällt damit aus....
    Ich würde, wenn die Daten im Speicher (Puffer) stehen, entweder eine einfache selbstgeschriebene Assembler- oder C- Dll fürs schreiben der "Pixel" in den Grafikpuffer nehmen und diesen dann per GDI "blitten" oder mit den GDI+-Funktionen weiterverarbeiten, smoothing usw. lässt grüßen.
    Somit könntest du die GUI und ggf das Datenloggen per USB mit AutoIt machen und müsstest nur noch 30x pro Sekunde die Dll-Funktion für die Darstellung der Frames aufrufen.

    /EDIT/ Gerade eukalyptus´ Post gelesen^^

    [autoit]

    Global $iPnt = 20

    [/autoit]

    Ist natürlich geschönt^^, aber selbst bei 200 Punkten erreicht man noch 30-40FPS
    Die Frage ist, ob Bezier-splines gewünscht sind^^

  • Gerade eukalyptus´ Post gelesen^^

    [autoit]

    Global $iPnt = 20

    [/autoit]

    Ist natürlich geschönt^^, aber selbst bei 200 Punkten erreicht man noch 30-40FPS
    Die Frage ist, ob Bezier-splines gewünscht sind^^

    Selbstverständlich ist das geschönt :D
    Das Script soll ja zeigen, wieviel fps GDI+/GDI schafft, und nicht, wie lange AutoIt benötigt, um die Daten zu verwerten...
    Um die Daten in das passende Format zu bringen, damit GDI+ etwas damit anfangen kann, würde ich sowieso nicht AutoIt verwenden (wenn es mehr als 200 Werte sind),
    sondern eine Dll dafür schreiben, oder FASM verwenden...

    Falls keine Glättung erwünscht ist, kann man auch das hier verwenden:

    [autoit]

    DllCall($ghGDIPDll, "uint", "GdipDrawCurve2", "hwnd", $hGfxBuffer, "hwnd", $hPen, "ptr", $pOsc, "int", $iPnt, "float", 0)

    [/autoit]


    oder auch das hier:

    [autoit]

    DllCall($ghGDIPDll, "int", "GdipDrawPolygon", "handle", $hGfxBuffer, "handle", $hPen, "ptr", $pOsc, "int", $iPnt)

    [/autoit]

    EDIT: Jetzt hab ich erst verstanden, was Andy gemeint hat :whistling:
    Auch die GDI+ Funktion dauert länger bei mehreren Punkten
    Ich komme bei 200 Punkten auf einen Framrate von 86fps...


    E

    • Offizieller Beitrag

    Hallo,

    @euka: Wow. Hätte ich nicht für möglich gehalten, habe hier ne Framerate von 130fps. Abgefahren... :D Der Vorteil ist tatsächlich, dass mir auch schon das direkt so in eine Struct gelegt wird von der API. Problem wird wahrscheinlich grade, dass nicht 20 Punkte sondern eigentlich 1024 Punkte geplant sind, da ist aber natürlich die Frage wie Sinnig das nachher ist, da man eh nicht alles auslesen kann. Bei 200 habe ich immer noch eine ausreichende Framerate. Das Ding ist halt, das Teil soll später nicht auf einem Quadcore laufen ;) Ein Atom wirds hoffentlich auch nicht, aber wie gesagt, kein High-End PC.

    Andy: Vielen Dank! 1HZ kann das Oszi leider nicht emulieren, das fängt erst ab 1000HZ an. Eigentlich sollte es so aussehen, wie im Bild im ersten Post.

    Ich greife direkt mit einer API (dll) auf die Box zu, das ist sehr schön gemacht und funktioniert Tadellos. Ich schätze wie gesagt, dass irgendwo noch ein Parameter falsch gesetzt ist. Das Teil geht bis in Femtosekunden bereich, wobei ich es für sehr unrealistisch halte, dass man das via USB auslesen kann ;) Für mich sind tatsächlich nur die millisekunden relevant. Mit den ganzen DLL Calls die von nöten sind (3 Stück pro Auslesen) bin ich ca. bei 2ms/Auslesen.

    Bei mir siehts dann mitterweile so aus, ist tatsächlich das gleiche Bild wie ich es vorher erzeugt habe, sauber :) Warum dies keine Sinuskurve ist, und warum es eigentlich egal ist, was ich für eine Kurve sende (Ob Sinus oder Block oder Dreieck), die Kurve trotzdem immer relativ gleich aussieht (aber auch nicht ganz gleich) ist mir zwar bisher ein Rätsel, aber da schau ich erstmal selbst und melde mich zu Not nochmal.

    Vielen vielen Dank erstmal an euch allen, hat mir wirklich sehr weiter geholfen :)

    Gruß
    Spider

  • Warum dies keine Sinuskurve ist, und warum es eigentlich egal ist, was ich für eine Kurve sende (Ob Sinus oder Block oder Dreieck), die Kurve trotzdem immer relativ gleich aussieht (aber auch nicht ganz gleich) ist mir zwar bisher ein Rätsel

    Vielleicht ist nur die Abtastrate zu niedrig, das könnte das Ergebnis erklären - siehe Bild:autoit.de/wcf/attachment/14667/

    Die Abtastrate muss mindestens doppelt so groß sein, wie die abzutastende Frequenz (deshalb auch 44100Hz bei einer Audio-CD um 20000Hz wiederzugeben ;))

    =============

    Um mehr Punkte zu verwenden, und trotzem eine höhere Framerate zu erzielen, kannst du auch einen zusätzlichen, kleineren Grafik-Buffer verwenden und diesen dann mit DrawImageRect auf die endgültige Größe skalieren.
    Das Ergebnis ist dann zwar nicht mehr so schön, aber mit ein paar Einstellungen (Smoothingmode, Interpolationmode) kann das ERgebnis durchaus annehmbar sein...

    E