[C++/Direct3D] Texturen Locken sehr langsam



  • hi

    Ich habe eine Anwendung, bei der ich in eine dynamische Textur selber zeichne. Präziser gesagt zeichne ich in ein char-array und kopiere dieses dann in die Textur. Der Zeichencode ist definitiv sehr effizient und beeinträchtigt die Performance nicht sehr stark. Aber in dem Moment, wenn ich die Pixeldaten zurück an die Graphikkarte sende und dafür die Textur locken muss, geht extrem viel Leistung verloren. Ich habe auch schon die Frequenz reduziert, in der das passiert. Dann wird es besser. Aber gut ist es immernoch nicht.

    Mich wundert auch, dass das reine Kopieren der Pixeldaten, was sogar noch mit Byte-order-shift passiert, die Leistung gar nicht beeinträchtigt und das bloße Holen des Pointers (LockRect) enorm an der Leistung frisst.

    Ich erzeuge die Textur folgendermaßen:
    D3DXCreateTexture( device, p2Width, p2Height, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &texture );

    Und LockRect wird so aufgerufen:
    texture->LockRect( 0, &d3drc, &dirtyRect, D3DLOCK_NO_DIRTY_UPDATE );

    Der LockRect Aufruf bekommt immer das gesamte verwendete Rechteck als Parameter (also alles abzüglich der power-of-two Aufrundungen) und um die Dirty Rects kümmere ich mich selber, damit ich wenigstens nur einmal locken muss.

    Mache ich irgendetwas grob falsch? Oder gibt es irgend einen Weg, das schneller hinzubekommen?

    Danke im voraus.

    Marvin


  • Mod

    wenn du warten musst bis die graphikkarte fertig ist, dauert es nunmal, und da die ein paar frames hinterher sein kann (bei 30fps z.b. 2*33ms -> 66ms), ist es langsammer als die meisten operationen.

    hier gab es schon einige threads dazu.

    lock ist immer evil.



  • Lock die ganze Textur (inklusive Aufrundungen), und gib das Flag D3DLOCK_DISCARD mit.
    Dadurch entfällt der Download der aktuellen Texturdaten. Das sollte einiges an Geschwindigkeit bringen.



  • Vielen Dank für Eure Antworten. Die waren extrem hilfreich. Insbesondere das mit dem D3DLOCK_DISCARD. Das hat extrem viel gebracht.

    Ich habe nicht ganz verstanden, wieso die Graphikkarte hinterherhinken kann. Ich rufe ja das Rendern der Textur über ein Sprite Frame für Frame auf. Macht die GraKa das asynchron? Das würde mich sehr wundern. In OpenGL ist das nicht so und da läuft sowas auch wunderbar schnell.

    Ich habe überlegt, ob es etwas bringen könnte, wenn ich zwei (oder mehr) Texturen erstelle und die immer abwechselnd verwende. Dann sollte die ja auf keinen Fall mehr in Benutzung sein, un der Lock entsprechend schnell gehen. Oder sehe ich as falsch?

    Marvin



  • Mir ist noch etwas aufgefallen. Wenn ich die Textur nicht in jedem Frame neu locke, was ich als Optimierung eingebaut habe, dann erscheint mit dem discard ständig flackernd Schnee auf dem Bildschirm.

    Die Doku sagt, dass ich bei D3DLOCK_DISCARD die gesamten Texturdaten überschreiben muss. Das wäre schlecht, da die Textur sehr groß ist.

    Die Textur bedeckt übrigens den gesamten Bildschirm. Also 1920x1200 plus die power-of-two Aufrundung.

    Komischerweise funktioniert es ohne das Überschreiben der gesamten Textur Frame für Frame, wenn ich sie in jedem Frame locke.

    Gibt es dafür eine Erklärung?

    Marvin


  • Mod

    Qudus schrieb:

    Vielen Dank für Eure Antworten. Die waren extrem hilfreich. Insbesondere das mit dem D3DLOCK_DISCARD. Das hat extrem viel gebracht.

    kurz die doku fuer dich

    Textures placed in the D3DPOOL_DEFAULT pool cannot be locked unless they are dynamic textures or they are private, FOURCC, driver formats. To access unlockable textures, you must use functions such as IDirect3DDevice9::UpdateSurface, IDirect3DDevice9::UpdateTexture, IDirect3DDevice9::GetFrontBufferData, and IDirect3DDevice9::GetRenderTargetData. D3DPOOL_MANAGED is probably a better choice than D3DPOOL_DEFAULT for most applications. Note that some textures created in driver-proprietary pixel formats, unknown to the Direct3D runtime, can be locked. Also note that - unlike textures - swap chain back buffers, render targets, vertex buffers, and index buffers can be locked. When a device is lost, resources created using D3DPOOL_DEFAULT must be released before calling IDirect3DDevice9::Reset. For more information, see Lost Devices (Direct3D 9).

    da steht auch schon viel nuetzliches fuer dich, denke ich.

    Ich habe nicht ganz verstanden, wieso die Graphikkarte hinterherhinken kann.

    weil sie asynchron laufen. du rufst auf "zeichen eine million dreiecke" und auch wenn deine funktion dazu sofort wieder zurueckspringt und dein programm weiter laeuft, wird die graphikkarte einiges an zeit beschaeftigt sein das abzuarbeiten. (das als beispiel!)

    Ich rufe ja das Rendern der Textur über ein Sprite Frame für Frame auf. Macht die GraKa das asynchron?

    frag mal andersrum, aus welchem grund sollte dein funktionsaufruf zum zeichnen solange warten und nichts tun bis sie mit dem zeichnen fertig ist?

    Das würde mich sehr wundern. In OpenGL ist das nicht so und da läuft sowas auch wunderbar schnell.

    das ist ueberall so, api unabhaengig. in opengl locks du vermutlich nicht, sondern rufst eine funktion auf die die textur updaten soll, die kopier dann all deine daten in einen commandbuffer und die graphikkarte holt sich die neue textur, wenn sie mit allem was du davor wolltest fertig ist.

    Ich habe überlegt, ob es etwas bringen könnte, wenn ich zwei (oder mehr) Texturen erstelle und die immer abwechselnd verwende. Dann sollte die ja auf keinen Fall mehr in Benutzung sein, un der Lock entsprechend schnell gehen. Oder sehe ich as falsch?

    das machen texturen wenn sie dynamic sind meistens, deswegen musst du sie mit D3DLOCK_DISCARD locken. weiter koenntest du managed nutzen und eine temporaere textur oder wie du moechtest, mehrere die du tauscht.


  • Mod

    Qudus schrieb:

    Mir ist noch etwas aufgefallen. Wenn ich die Textur nicht in jedem Frame neu locke, was ich als Optimierung eingebaut habe, dann erscheint mit dem discard ständig flackernd Schnee auf dem Bildschirm.

    Die Doku sagt, dass ich bei D3DLOCK_DISCARD die gesamten Texturdaten überschreiben muss. Das wäre schlecht, da die Textur sehr groß ist.

    Die Textur bedeckt übrigens den gesamten Bildschirm. Also 1920x1200 plus die power-of-two Aufrundung.

    Komischerweise funktioniert es ohne das Überschreiben der gesamten Textur Frame für Frame, wenn ich sie in jedem Frame locke.

    Gibt es dafür eine Erklärung?

    Marvin

    vielleicht sind dann noch die daten vom letzten oder vorletzten frame drinnen, sodass dir kaum auffaellt dass da muell steht, eigentlich.

    lass D3D im debug modus laufen, dann koennte es sein, dass du die fehler eher siehst.



  • rapso schrieb:

    kurz die doku fuer dich
    da steht auch schon viel nuetzliches fuer dich, denke ich.

    Die kenne ich. Allerdings hat sie mich eher verwirrt als dass sie geholfen hat. Im Grunde steht da, dass ich POOL_MANAGED verwenden sollte. Allerdings kann ich das gar nicht, wenn ich USAGE_DYNAMIC auswähle, da diese beiden Parameter nicht verträglich sind. Und ein RenderTarget will ich ja nicht haben. Leider sind das die beiden einzigen USAGE-Parameter. Daher verstehe ich das nicht.

    Irgendwie kommt mir Direct3D bedeutend schraddeliger vor als OpenGL und da ist die API schon sehr gewachsen.

    Gibt es denn noch eine Möglichkeit, nur die veränderten Teile der Textur zu überschreiben, ohne dass ich den Rest in den Hauptspeicher zurück lade? Also DISCARD aber ohne, dass wirklich verworfen wird. Also im Grunde ein write-only Zugriff.

    Marvin



  • Ist LockRect tatsächlich die einzige Möglichkeit, Texturedaten in D3D zu manipulieren? Ich habe bei diesem Weg nur die Möglichkeit, entweder mit DISCARD die gesamte Textur zu locken, was mit auf der einen Seite Performancevorteile bringt, weil nichts zurückübertragen wird (was ich ja nicht brauche), aber auf der anderen Seite extreme Performanceeinbußen mit sich bringt, weil ich die gesamte Textur neu befüllen muss. Oder ich locke ohne DISCARD nur die benötigten Rectangles, das dauert aber extrem lange, weil die Daten erst zurückübertragen werden müssen und außerdem noch evtl. auf die liebe GraKa gewartet werden muss.

    Im Grunde will ich ja nur der GraKa die neuen Daten geben und wann und wie sie die überträgt, ist mir egeal. Bei OpenGL geht das wunderbar. Und da die APIs beide auf die gleichen Hardwareschnittstellen zugreifen (sollten), sollte das ja eigentlich auch mit D3D möglich sein.

    Bei OpenGL gibt es sogar eine Extension, mit der ich direkt auf die Pixeldaten zugreifen kann, ohne dass ich irgendetwas übertragen muss. Laut Doku habe ich da einfach einen Buffer mit direktem Zugriff auf die Pixelbits. Ausprobiert habe ich das allerdings noch nicht. Gibt es das vielleicht auch bei D3D?

    Es muss doch irgendeine Methode geben, um Pixeldaten effizient auf die GraKa zu übertragen? So exotisch kann das doch nicht sein. Wenn schon auf DirectDraw in D3D9 verzichtet wird, muss es doch einen Weg geben. Oder verfolge ich den komplett falschen Ansatz?

    Marvin



  • Du kannst UpdateTexture verwenden.

    Probier mal ob es geht wenn du eine System-Memory Texture anlegst, und eine "normale" (Default Pool) Texture.
    Dann lockst du die System-Memory Texture (was mit "zero overhead" gehen sollte - OHNE DISCARD Flag), und machst da deine Änderungen. Danach kopierst du die Änderungen mit UpdateTexture in die andere Textur rüber.

    Du solltest allerdings nur die Bereiche der System Memory Texture "locken", die du auch updaten musst, sonst wird die gesamte Textur als "dirty" markiert (und dadurch bei UpdateTexture als ganzes kopiert). Oder du verwendest wieder dieses "no dirty" Flag, und markierst die Bereiche die du geändert hast selbst mit AddDirtyRect.



  • Guter Tipp. Werde ich mal ausprobieren. Vielen Dank. (Mann das wird eine Speicherfräse 🙂 )

    Marvin



  • Vielen Dank nochmal für den Tipp. Leider hat das auch nichts gebracht. Keine Performancevorteile und sogar eher langsamer. Hmm... ich hoffe echt, dass ich dazu noch eine Lösung finde.

    Marvin



  • Kann ich mir grad garnicht vorstellen. Beschreib mal bitte was du genau gemacht hast...
    p.S.: und: keine Performance-Vorteile gegenüber was? Gegenüber der "Version-mit-Flimmern" oder gegenüber der Version die du ganz zu Anfang hattest?


Anmelden zum Antworten