Schneller "blitten" per OpenGl/D3D ?

  • Hi zusammen,
    ich hatte schon in der Vergangenheit festgestellt, daß "blitten" nur bei kleinen Größen wirklich schnell ist.
    Bei Größen von z.B. 1680x1050 in 32Bit Farbtiefe dauert ein BitBlt() ca. 100ms. Wie es aussieht, wohl relativ unabhängig von der Rechnerperformance mal bissl mehr, mal weniger.
    Nun die Frage an die OpenGl/D3D-Spezialisten:
    Gegeben folgendes Szenario:
    Veränderungen auf dem Desktop sollen detektiert und "irgendwie" ausgelesen werden. Zur Zeit ist das so gelöst, daß der Desktop in einen Speicherbereich "geblittet" wird und dort mit dem aktuellen Desktopinhalt verglichen wird.
    Der Vergleich incl. Feststellen der veränderten Pixel incl. Komprimierung wird per ASM-code erledigt und dauert ca 1ms. Das "Blitten" vom Grafik- in den Hauptspeicher dauert aber 100ms. Das ist für Sprenger120´s "DeskStream" viel zu langsam, da so nur maximal 10FPS erreicht werden können, mehr gibt der Server aufgrund des langsamen Blittens nicht her.

    Gibt es eine Möglichkeit per OpenGl/D3D (oder anderem Verfahren) schneller an die unterschiedlichen Pixel von 2 aufeinanderfolgenden Frames auf dem Desktop zu kommen? Das Format wäre erstmal egal, wichtig wäre nur, diese Daten schnellstmöglich in den HAUPTSPEICHER zu bekommen, um sie dann per TCP zu versenden.

    /EDIT/ Das eigentliche Blitten geht bissl schneller, aber das "schöne" Blitten mittels SetStretchBltMode(0) dauert ziemlich lange.

    /EDIT2/ Ich wiederufe alles und behaupte das Gegenteil! Solange nicht gleichzeitig massive Speicherbewegungen stattfinden (bzw "Action" auf dem Bus ist) ist Blitten sehr schnell!
    StretchBlt() ist dagegen, vor allem noch im "schönen" Modus, schnarchlangsam^^, siehe Beispielscript

    Spoiler anzeigen
    [autoit]

    #include <WinAPI.au3>

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

    $ibreite = @DesktopWidth
    $ihoehe = @DesktopHeight

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

    Global $fps
    $hDesktopDC = _WinAPI_GetDC(0) ;dc desktop

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

    AdlibRegister("_fps",1000)

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

    $hgui=guicreate("",1000,800)
    $hguiDC=_WinAPI_GetDC($hgui)

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

    guisetstate()
    ;$Ret = DllCall("gdi32.dll", "int", "SetStretchBltMode", "dword", $hguiDC, "int", 4)

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

    While 1;GUIGetMsg()<>-3
    ;_WinAPI_StretchBlt($hguiDC, 0, 0, 1000,800, $hDesktopDC, 0, 0,@DesktopWidth,@DesktopHeight,0x00CC0020); $SRCCOPY
    _WinAPI_BitBlt($hguiDC, 0, 0, $ibreite, $ihoehe, $hDesktopDC, 0, 0,0x00CC0020); $SRCCOPY
    $fps += 1
    WEnd

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

    Func _fps()
    ToolTip("FPS=" & $fps)
    $fps = 0
    EndFunc ;==>_fps

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

    Func _WinAPI_StretchBlt($hDestDC, $iXDest, $iYDest, $iWidthDest, $iHeightDest, $hSrcDC, $iXSrc, $iYSrc, $iWidthSrc, $iHeightSrc, $iRop)

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

    Local $Ret = DllCall('gdi32.dll', 'int', 'StretchBlt', 'hwnd', $hDestDC, 'int', $iXDest, 'int', $iYDest, 'int', $iWidthDest, 'int', $iHeightDest, 'hwnd', $hSrcDC, 'int', $iXSrc, 'int', $iYSrc, 'int', $iWidthSrc, 'int', $iHeightSrc, 'dword', $iRop)

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

    If (@error) Or (Not $Ret[0]) Then
    Return SetError(1, 0, 0)
    EndIf
    Return 1
    EndFunc ;==>_WinAPI_StretchBlt

    [/autoit]

    ciao
    Andy


    "Schlechtes Benehmen halten die Leute doch nur deswegen für eine Art Vorrecht, weil keiner ihnen aufs Maul haut." Klaus Kinski
    "Hint: Write comments after each line. So you can (better) see what your program does and what it not does. And we can see what you're thinking what your program does and we can point to the missunderstandings." A-Jay

    Wie man Fragen richtig stellt... Tutorial: Wie man Script-Fehler findet und beseitigt...X-Y-Problem

    4 Mal editiert, zuletzt von Andy (25. Januar 2011 um 20:15)

  • Ein Rescale dauert eben immer seine Zeit. Eine 1:1-Kopie sollte viel schneller gehen.
    Vielleicht hast du ja eine Idee, wie du das schneller lösen kannst per ASM. Dann brauchst du nur noch ein nromales BitBlt machen.

  • Ich hatte das schonmal angesprochen, aber jetzt fällt es mir wieder auf, da ich ein Spiel in der Auflösung 800x500 px (16:10) machen will. (Bricks oder Winbrick falls das jemand kennt. sowas soll es mal Werden)

    Ein Graphicsclear bei der Auflösung dauert 0.5ms
    Davon brauche ich 4 Stück. (Front, Mid und Backbuffer und ein kleiner Puffer für die Titelleiste)
    Damit sind schon 2ms verloren

    Ein BitBlt dauert ca. 1.1 ms. Davon brauch ich ebenfalls 4. (2x Alphablend, 2x BitBlt)
    Erst Mid auf Back, dann Front auf Back, dann Back auf das Fenster und einen für die Titelleiste.
    Damit sind auch 4.4ms im Eimer.

    mit kleinen Nebensächlichkeiten komme auch so auf ca. 7ms. NUR für das Zeichnen.
    Das ist eine Zeit die viel zu hoch ist wenn man bedenkt, dass das ausschließlich das Auftragen der Puffer auf das Fenster beinhaltet. Da sind keinerlei Bilder und nix eingeschlossen bisher.

    Ich bräuchte in asm
    BitBlt
    AlphaBlend
    FillRect
    Clear
    (BitBlt und AlphaBlend brauche ich nur OHNE Resize. also einfach 1:1. das geht dann auch viel schneller und braucht weniger Parameter)

    Ich nutze ja die Funktionen die auch die ptr liefern. (von dir). Man kann also direkt mit asm arbeiten.

    schau es dir mal an bitte. Sooo viel Arbeit kann ein einfacher Transfer ja nicht sein im Vergleich zu der Trapezoid Geschichte^^
    (vom Clear ganz zu schweigen. Das dauert für dich zum Erstellen der Funktion wahrscheinlich keine 5Minuten.)

    lg
    Mars(i)

  • Zitat

    mit kleinen Nebensächlichkeiten komme auch so auf ca. 7ms. NUR für das Zeichnen.

    aha, und was ist daran schlecht?

    Zitat

    Das ist eine Zeit die viel zu hoch ist wenn man bedenkt, dass das ausschließlich das Auftragen der Puffer auf das Fenster beinhaltet. Da sind keinerlei Bilder und nix eingeschlossen bisher.

    Ich finde die Zeit völlig in Ordnung....

    Zitat

    Ich bräuchte in asm
    BitBlt
    AlphaBlend
    FillRect
    Clear

    na wenn das alles ist, gib mir mal schnell die passenden Einsprungpunkte in den Kernel und das Protokoll des universalen Grafiktreibers für sämtliche möglichen Windows-Bildschirmauflösungen. Weiterhin hätte ich gerne die komplette Treiberdokumentation orginal von Microsoft. Natürlich für XP,Vista und Win7 sowohl in 32Bit- als auch in 64Bit. Und wenn du die Dokumentation hast, leg direkt noch die Telefonnummer von einigen MS-Programmierern dazu, die sich damit wirklich auskennen!
    Die Zeiten, in denen man "einfach" im Grafikspeicher rumgeschrieben hat, sind seit 20 Jahren vorbei. Wenn du willst, kannst du ja die good ol´times wieder aufleben lassen....16Bit-Modus, Grafikspeicher ab 0xA000 und gib ihm....

    [autoit]


    FileDelete("demo.com")
    filewrite("demo.com",binarytostring("0xB81300CD106800A00711C8AAE2FB40EBF8"))
    run ("demo.com")

    [/autoit]
    Zitat

    Man kann also direkt mit asm arbeiten.

    kann man allerdings, wenn man Lust und Zeit hat und sonst nichts zu tun...

    Zitat

    Sooo viel Arbeit kann ein einfacher Transfer ja nicht sein im Vergleich zu der Trapezoid Geschichte

    Bei Trapezoid hänge ich auch noch, solange es keine Möglichkeit gibt, den langsamen Lock zu umgehen um an die Bitmapdaten zu kommen, bringt das alles nicht viel...ausserdem wird in Trapezoid nirgendwohin transferiert, lediglich die Bitmap wird im Hauptspeicher etwas bearbeitet. Alle anderen Funktionen sind reine GDI!

    Zitat

    (vom Clear ganz zu schweigen. Das dauert für dich zum Erstellen der Funktion wahrscheinlich keine 5Minuten.)

    Es gibt in der WinAPI schon fix- und fertige Funktionen, sogar in AutoIt gewrappert!

    [autoit]

    _MemMoveMemory()

    [/autoit]

    macht sowohl das Clear als auch den Transfer, jedenfalls zwischen den Backbuffern.

    Btw, kannstest du noch nicht den den Spruch: "Nur selbst fressen macht fett?"

  • Ich kenne den Spruch. Ich kann aber kein asm^^

    Und wenn man Etwas nicht kann muss man es entweder lernen, oder jemanden Finden der sich damit auskennt und schauen ob er es vllt machen kann.

    Bei BitBlt (im Puffer) braucht man glaube ich keinerlei Treiberdaten oder sonstwas, da es nur ein Transfer ist.
    Gleiches gilt für AlphaBlend.
    Nachdem alles zusammengelegt ist kann mans ja immernoch mit der Standardfunktion auf den Bildschirm bringen.
    (Dann hat man nur 1Mal den hohen Zeitaufwand)
    Der Clear soll ja auch nur Im Puffer stattfinden. (dafür schaue ich mir mal die MoveMemory func an. Das blitten kann die aber nur auf der untersten Ebene ersetzen. Aber auch das probier ich mal aus.).
    Genau so wie das FillRect.
    Im Prinzip braucht man ja nur Ein Mal das Bild ins Fenster schieben.

    Und da habe ich echt gedacht dass das kein großer aufwand ist das "mal eben" zu machen.
    (Wie gesagt. Ich kann kein asm und zum Lernen ist mir das zu verworren. Aber ich weiß, dass ein Paar Zeilen wahre Wunder vollbringen wenn sie richtig sind.)

    Ich wollte Dich nicht "angreifen"...^^

    lg

  • Hi,

    Zitat

    Bei BitBlt (im Puffer).......Gleiches gilt für AlphaBlend.

    ach sooooo^^, gewissermassen schnelle(re) Speicherschiebebefehle innerhalb des Hauptspeichers (liegen alle Backbuffer im Hauptspeicher?)?

    Das "Clear" ist dann ja soweit klar? Einfach ein MemMove eines "leeren" Puffers in einen der Backbuffer. Da lohnt keine extra asm-Funktion, die API-Variante ist nur einige Takte langsamer, aber das ist die Mühe echtnicht wert!

    Zitat

    Genau so wie das FillRect.

    Ein rechteckiger Speicherbereich soll mit einem anderen gefüllt werden? Also äquivalent zur Bitblt()-Funktion?

  • S. /EDIT2/ im 1. Post!

    @progandy,
    hehe, ich hatte schon geahnt, dass mir jemand den "Vorschlag" macht, einen Renderer in ASM zu basteln, aber du hast natürlich Recht! Langsamer als StretchBlt() kanns nicht werden :thumbup: Wollte schon immer mal in bileneare Filter und das andere Gedöns eintauchen....

  • Jaa ;)
    Alle Grafiken liegen im Hauptspeicher, und sollen auch in diesem Verwendet werden. Das Abbilden aufs Fenster mache ich mit einem Standard BitBlt.

    Ich habe die MemMove Func mal probiert.
    Ich bin mir nicht sicher, ob ich iwas falsch mache dabei, aber sie dauert 1.3ms bei 800x500px... ein Clear braucht 0.5ms
    Als Größe für das zu verschiebende Memory habe ich das Vierfache der Pixelzahl genommen. (weil AARRGGBB in Hex = ARGB in asc).
    Ich habe es so hinbekommen ein Bild auf einen Puffer zu übertragen. Die Breite und höhe muss aber gleich sein. (ist mir aber egal, da Hintergrundbilder oder Clears sowieso auf den kompletten Puffer wirken sollen)

    mit FillRect meine ich im Prinzip das gleiche wie beim Clear, Nur mit xy offset und Breite und Höhe.
    Was beim Clear schön wäre ist, dass man z.B. als Farbe 0x50AABBCC eingeben kann und der zu löschende Puffer anschließend auch diese Farbe hat. beim GraphicsClear klappt das nicht. Da kann man entweder 100% Transparent (mit 0x00xxxxxx) oder ohne Transparenz den Speicher "leeren". Wenn man Teiltransparenz nimmt wird nur mit der angegebenen Farbe übermalt...

    //:.Edit.: Habe grade festgestellt, dass ein MemMove nahezu gleichschnell ist wie ein Standard BitBlt. Die machen ja auch fast das Selbe. Man kann also in asm mit dem clear noch weeesentlich mehr rausholen, weil ja nichts kopiert werden muss. Wozu einen Puffer auslesen und in einen anderen schreiben, wenn man das was man schreiben will doch schon kennt ? (einfarbige Füllung mit argb) .://

    lg

  • [autoit]

    Func _ASM_Clear()
    _('use32')
    _('mov esi,dword[esp+4]') ;Startadresse
    _('mov ecx,dword[esp+8]') ;anzahl Pixel
    _('mov ebx,dword[esp+12]') ;ZielFarbe
    _('_schleife:') ;bis ecx=0
    _('mov dword[esi],ebx') ;Pixel färben
    _('add esi,4') ;nächstes Pixel
    _('sub ecx,1') ;runterzählen (schleife)
    _('ja _schleife') ;so lange, bis ecx=0
    _('ret ')
    EndFunc

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

    Global $ASM_Struct_1 = _AsmToStruct('0x8B7424048B4C24088B5C240C891E83C60483E90177F6C3')

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

    Func _AsmToStruct($ASM_Code)
    Local $CodeStruct = DllStructCreate('byte['&StringLen($ASM_Code)/2-1&']')
    DllStructSetData($CodeStruct, 1, $ASM_Code)
    Return $CodeStruct
    EndFunc

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

    Func _ASM_GraphicsClear($ptr, $len, $col)
    ;~ _AssembleIt("ptr","_ASM_Clear","ptr", $ptr, "int", $len, 'int', $col) ;um den code zu erhalten.
    DllCall($h_USER32_DLL, 'ptr', 'CallWindowProcW', 'ptr', DllStructGetPtr($ASM_Struct_1), 'ptr', $ptr, 'int', $len, 'int', $col, 'int', 0)
    EndFunc

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


    Macht genau das was ich brauche :)
    Dauert nur halb so lange wie ein normaler Clear. (bei mir statt 0.5ms nur 0.25)
    (also mit Aufruf und GetPtr. Die Fertige Struct habe ich schon zwischengelagert. Dann gehts schneller)
    SELBST gebaut xD juhuu :)

    Edit:
    Schneller als BitBlt (Nit Blackness) ist das allerdings nicht. Das braucht schlappe 0.19ms^^
    Ein Kopie von _MemMoveMemory habe ich auch gemacht. Leider bin ich nur halb so schnell.....
    Ich weiß nicht wie ich das gemessen habe mit den 1.3ms. Jetzt braucht ein MemMove grade noch 0.25ms.^^
    Alles ist komisch... :/^^

    lg
    Mars(i) :P