Registriert: Di Okt 03, 2006 14:07 Beiträge: 1277 Wohnort: Wien
Ich bin grade damit beschäftigt, mein TEdit zu verbessern. Im Zuge dessen bin ich mir wieder mal meines Untergrundes bewusst geworden, nämlich die Schrift. Ich verwende eine Texture Font, die ich mir per FreeType aufbaue und das Drucken erledige ich mit glCallLists (Textur+Displaylisten). Funktioniert gut. Nicht ganz so schnell wie Windows, aber bei normaler Tippgeschwindigkeit gibts keine Probleme, etwas ins TEdit hineinzuschreiben. Bei ultraschnellem Unsinn-Tippen läuft er allerdings nach, soll heissen er schreibt noch ein wenig weiter, nachdem ich mit dem Tippen aufgehört habe, das tut Windows nicht.
Nun hat mir mal jemand gesagt: "glCallLists - das ist doch total langsam, da gibt es doch viel schnellere Methoden".
Ist das wirklich so und wenn ja, welche Methoden sind das?
Registriert: Do Dez 05, 2002 10:35 Beiträge: 4234 Wohnort: Dortmund
Also zu kleine Displaylisten wirken sich sehr negativ auf die Performance aus, da intern dann seperate VBOs für 4 Vertices erstellt und benutzt werden. Also entweder solltest du die Zeichen imediate zeichnen oder du packst die Zeichen des Textes selber in ein VBO. In meinen Fonts arbeite ich aktuell noch imediate aber in der nächsten Version werd ich das per VBO machen.
Evtl gibts ja auch die Möglichkeit, dass du nicht bei jedem KeyDown neu zeichnest sondern evtl mehr KeyDowns verarbeitest. Was sich in der VCL Programmierung bei mir bewehrt hat ist ein DrawCounter. Also bei jedem Draw den Counter erhöhen und mit ausgeben. Wenn es nicht gerade ein Renderloop ist erkennt man sehr schnell, dass in der Regel viel zu häufig neu gezeichnet wird.
Ich habe in den letzten Tagen für eine andere Anwendung 10.000 unterschiedliche DisplayListen mit jeweils einer Linie gezeichnet und keinerlei Geschwindigkeitsprobleme beim Zeichnen festgestellt (Geforce 8400). 10.000 Bufferwechsel pro Frame wären vermutlich zu viel.
Registriert: Di Okt 03, 2006 14:07 Beiträge: 1277 Wohnort: Wien
Ich zeichne nicht zuviel, eher zuwenig. Ich habe für die GUI-Items ein "Invalid" eingeführt. Jedes GUI-Element bestimmt individuell, ob es neu gezeichnet werden muss. Das TEdit entscheidet, dass nach Empfang eines Keys (Function Keys wie Cursortasten etc. und druckbare Zeichen) jedenfalls neu gezeichnet werden muss. Der User will, wenn er neues Zeichen eingibt, dieses Zeichen auch sehen.
@Lossy: das mit dem Counter werd ich beim Testen trotzdem berücksichtigen. Es sollte eigentlich nur soviel zeichnen, wie notwendig, aber - man weiß es erst, wenn man es nicht nur konzipiert, sondern auch überprüft hat. Und ich habe in letzter Zeit so viele Umstellungen gemacht, dass überprüfen doch vielleicht angesagt ist.
Das Umstellen auf VBO's möchte ich deswegen nicht machen, weil ich nicht überzeugt bin, dass auf allen Grafikkarten VBO zur Verfügung steht (Laptops?). Die Schrift, die ich zur GUI dazugebe, hat nur die Funktion eine Grundausstattung für alle Fälle zu sein. Weil die einzelnen User vermutlich lieber ihre eigene Schrift verwenden wollen, die mehr kann, als nur Text darstellen. Dafür soll sie aber überall funktionieren.
Das ist also offenbar wieder systemverschieden. Hmm. Ich werde also das Testen mit VertexArrays auf meine ToDo-Liste schreiben. Wenns auf meinem System schneller ist als die Displaylisten (die sind ja wirklich klein) stelle ich das um.
Registriert: Do Dez 05, 2002 10:35 Beiträge: 4234 Wohnort: Dortmund
Lars: Bei meiner ATI (war glaube ich noch die X800) kann ich mit Gewissheit sagen, dass zu kleine Displaylisten performance kosten. Ich habe bei meinen Fonts ein Beispiel welches einen kleinen Text zeichnet und die FPS misst. Da habe ich es mal von einzelnen texturierten Quads auf einzelne DisplayListen umgestellt. Und die FPS ist von Imediate ca. 800 auf unter 600 mit Displaylisten gesunken. Immer noch vollkommen okay. Aber das vermeidlich schnellere war dann langsamer.
Traude: Die Texturfonts. Sind die schon komplett erstellt wurden oder erzeugst du die dann, wenn die Zeichen benötigt werden? Fällt mir nämlich gerade so ein. Wenn du die zur Laufzeit erstellt, dann wenn sie benötigt werden könnte evtl das Erstellen mit FreeType bereits zu einem Engpass führen. Dann läge das evtl. gar nicht bei OpenGL sondern bereits beim Erstellen der Glyphen.
VBO: Also ob VertexBufferObjects von Laptops sinnvoll unterstützt wird weiß ich nicht genau. NVidia emuliert VBOs selbst aber auf TNT2 (hat keinen Vertexspeicher). Was im Endeffekt nur wieder auf VertexArrays hinausläuft und die werden bereits seit OpenGL 1.1 unterstützt. Könnte man also so machen dass VAs benutzt werden wenn VBOs nicht unterstützt werden. Dann bleibt der Speicher aber nur lokal. Aber da weiß ich nicht wie das in dein Konzept und deinen Plan passt. Weswegen diese Entscheidung bei dir liegt.
Zeichenaufrufe: Ich meinte das so, wenn du zum Beispiel gerade zeichnest und in der Zwischenzeit 2 Tastaturevents bekommst dann könntest du beide Events gleichzeitig verarbeiten und erst danach neu zeichnen. Dann hättest du keine Verzögerung beim Zeichnen sondern einen kleinen "Sprung". Aber das kommt stark auf die Verarbeitung der Events an. Ich denke ohne eine Queue oder vergleichbares wird das vermutlich nicht gehen. Zu mindest fällt mir aktuell nicht ein wie.
Counter: Das ist so eine Sache die ich bei grafischen VCL Sachen mittlerweile konsequent mache. Selbst wenn es schnell genug ist ist es teilweise doch erschreckend an welchen Stellen man da Updateunterdrückungen vergessen kann.
Registriert: Di Okt 03, 2006 14:07 Beiträge: 1277 Wohnort: Wien
Die Texture Fonts erstelle ich beim Create der GUI. Während der Laufzeit ist schon alles in Texturobjekte und Displaylisten verpackt, die bereits auf der Grafikkarte sind. Von daher sollten es eigentlich keine Probleme geben. Ich habe keine so tolles System wie ihr: ein 960 MH Prozessor eine uralte GeForce DDR und nur 128 MB RAM. Also Frameraten wie 800 sind für mich nicht erzeugbar; wenn ich auf 100 komme, bin ich schon froh.
Das Zeichnen funktioniert so: TGUIEdit kriegt eine Eingabe und setzt sich auf "Invalid". Dann benachrichtigt er das Fenster, dass neu zu Zeichnen ist (dann kann er direkt, er hat einen Zeiger auf sein Window). Das Window leitet einen Zeichenvorgang ein (eine TREE-Rekursion), in dem alle Items, die "Invalid" sind, gezeichnet werden. Das geht in der CPU vor sich und sollte blitzschnell sein.
Was mir gerade so einfällt: vielleicht sollte ich mal einen Eventfilter einbauen? Manchmal ist nichts los, aber dann wird man mit Events geradezu beschossen. Könnte doch sein, dass nicht alle relevant sind? Das muss ich mal überprüfen. Bei den anderen Items ist mir das nicht so aufgefallen, beim TEdit ist das viel krasser, denn der muss ziemlich viele Events verarbeiten.
Registriert: Di Okt 03, 2006 14:07 Beiträge: 1277 Wohnort: Wien
Beim TGUIEdit zeichnet er pro gedrückter Taste. Eine Scrollbar zum Beispiel muss per MausMove zeichnen. Das Elternelement der Scrollbar - sagen wir mal eine Listbox - könnte jetzt z.B. ein Tree-Invalid aufrufen, wobei es sich selbst invalid setzt und auch alle seine Kinder, also auch die Scrollbar (wenn die Listbox neu gezeichnet wird, muss wohl oder über auch die darauf befindliche ScrollBar neu gezeichnet werden). So habe ich es im Augenblick. Das heißt aber nicht, dass das Elternelement gezeichnet werden muss, wenn ein Kindelement invalid ist.
Ich wollte damit erreichen, dass ausschließlich dann gezeichnet wird, wenn es notwendig ist. Ans Zusammenfassen von Events hatte ich eigentlich nicht gedacht. Da müßte man sinnvoll miteinander vorkommende Tastenkombinationen zusammennehmen oder Events rausschmeißen, wenn sie als nicht sinnvoll erkannt werden. Aber das ist in der derzeitigen Implementierung nicht enthalten.
Registriert: Di Mai 18, 2004 16:45 Beiträge: 2623 Wohnort: Berlin
Programmiersprache: Go, C/C++
Also da muss was in der Umsetzung nicht stimmen, denn ich konnte in meinen Editfeldern die Taste auf anschlag halten und dann wird ja die windows repeat funktion genommen und das zeichen in einen konstanten abstand als message geschickt(0.2sek glaube ist der defaultwert).
Gehst du auch sicher, dass deine Fontmap 1. npot ist, 2. nur eine pro alle paar schriftgrößen(eine für 8px,12px,16px z.B.) und somit nicht ständig die Texturwechsel hast und 3. die displaylist auch korrekt erstellt ist(vieleicht zeichnest du z.B. mit jedem weiterem Zeichen n-1 Zeichen, also bei 4Zeichen 4+3+2+1).
Wobei, es gibt noch zwei schwachstellen, dein Puffer und Messagehandling.
Das beim hinzufügen von Buchstaben vieleicht ein zu hoher overhead an prüfungen entsteht oder der Code bis zum endgültigem hinzufügen in die Zeichenkette viel zu lang ist.
Als ich jemandem im IRC geholfen hab, hatte er perfprobs mit seiner Memo Komponente.
Er hat bei meheren 100 Zeilen Text sich gewundert, wieso es langsamer geworden ist, als wenn er z.B. 30Zeilen hat.
Das Problem lag an meheren Stellen, er hatte erstens die Speicherung über char* gemacht(absolutes no no TStringList oder für c++ halt vector<string>), durch zeilenanzahl,clientrect und fontheigt kann man die erste und letzte sichtbare Zeile sowie Zeichen leicht berechnen und zum schluss hat er auch noch mehr gezeichnet als notwendig.
Danach hat er einfach ein Buffer für die Darstellung zur verfügung gestellt, wo er nur den ausschnitt an Text reinkopiert hat(von zeilenanfang bis zeilenende per for und ein copy von Zeichenanfang bis Zeichenende(benötigt eine zeichenweise verarbeitung der zeichenbreite)), damit wurden dann auch zeichen links und rechts von einer Zeile nicht übernommen und garnicht erst gezeichnet. Macht man das nicht und das Editfeld kann maximal 255 Zeichen fassen, dann wäre bei einem vollem Editfeld ne menge overhead, da man ja 255 zeichenaufe hat statt nur die 26(wilkürliche zahl).
_________________ "Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren" Benjamin Franklin
Ich habe für die GUI-Items ein "Invalid" eingeführt. Jedes GUI-Element bestimmt individuell, ob es neu gezeichnet werden muss.
Ich muss hier grad mal ne ganz dumme Frage stellen: was soll das heissen, es entscheidet slebst ob es gezeichnet werden muss?
also wenn ich einen renderdurchlauf starte, dann muss ich bei mir immer alles rendern, sonst wird es eben nicht angezeigt, da kann ich nich einfach irgendwas weglassen. also würd ich mein editfeld der gui jetz sagen lassen "ich muss nich gezeichnet werden, ich hab mich eh nicht verändert" dann wäre es beim naechsten rendern weg
Registriert: Di Okt 03, 2006 14:07 Beiträge: 1277 Wohnort: Wien
Tak2004 hat folgendes geschrieben:
Zitat:
Also da muss was in der Umsetzung nicht stimmen, denn ich konnte in meinen Editfeldern die Taste auf anschlag halten und dann wird ja die windows repeat funktion genommen und das zeichen in einen konstanten abstand als message geschickt(0.2sek glaube ist der defaultwert).
Das muss ich noch überprüfen. Das Fensterelement reagiert natürlich auf eine Paint-Botschaft; Das TGUIEdit reagiert aber auf den Tastencode, der hereinkommt. Hier könnte sich tatsächlich etwas spießen, denn irgendwie gehören die beiden zusammen.
Zitat:
Gehst du auch sicher, dass deine Fontmap 1. npot ist, 2. nur eine pro alle paar schriftgrößen(eine für 8px,12px,16px z.B.) und somit nicht ständig die Texturwechsel hast und 3. die displaylist auch korrekt erstellt ist(vieleicht zeichnest du z.B. mit jedem weiterem Zeichen n-1 Zeichen, also bei 4Zeichen 4+3+2+1).
Meine Fontmap ist pot, das berechne ich extra vorher und mein System würde sie gar nicht anzeigen, wenn sie es nicht wäre. Beim Testen verwende ich nur eine einzige Textur, es kann daher auch keine Texturwechsel während des printens geben.
Zitat:
Wobei, es gibt noch zwei schwachstellen, dein Puffer und Messagehandling. Das beim hinzufügen von Buchstaben vieleicht ein zu hoher overhead an prüfungen entsteht oder der Code bis zum endgültigem hinzufügen in die Zeichenkette viel zu lang ist.
Beim Hinzufügen von Buchstaben passiert folgendes (mit Debugger überprüft!): Ein Buchstabe wird von Windows angeliefert. (Übrigens: Ich verwende einen WideString als Textpuffer). Das bedeutet, dass er an der Cursorposition eingefügt werden muss. Mach ich einfach mit der Routine "Insert". Anschließend erfolgt die Neupositionierung des Cursors und dann wird das Scrollen im Editierfenster ausgeführt, wenn nötig. Dann setzt er sich selbst auf "invalid" und ruft die TREEPaint-Methode des Fensters auf. Diese befragt dann alle Elemente, ob sie gezeichnet werden müssen. Sollte außer ihm keiner "invalid" sein, wird eben nur er gezeichnet. Außer natürlich, wenn Windows zusätzlich mit Paint-Botschaften feuert. Da könnte es natürlich sein, dass zu oft gezeichnet wird.
Zitat:
Macht man das nicht und das Editfeld kann maximal 255 Zeichen fassen, dann wäre bei einem vollem Editfeld ne menge overhead, da man ja 255 zeichenaufe hat statt nur die 26(wilkürliche zahl).
Nein, das mach ich ganz sicher nicht. Ich habe sogar viel Zeit in die Optimierung einer Methode namens "CalcVisibleTextSize" investiert. Man kann leicht überprüfen, ob er über den Elementrand hinausschreibt, wenn man Scissor abschaltet.
Danke auch für Deine Antwort. Für mich sieht es jetzt so aus, als ob ich zuviele Boschaften empfange und verarbeite.
Shaddow hat folgendes geschrieben:
Zitat:
was soll das heissen, es entscheidet slebst ob es gezeichnet werden muss?
Stell Dir mal vor, die hast ein normales rechteckiges Fenster. Auf dem Fenster ist ein Panel, das nur einen Teil des Fensters bedeckt. Auf dem Panel ist ein Editierfeld. Der Benutzer gibt gerade Buchstaben ein. Auf den Editierfeld ist nicht der ganze String zu sehen, sondern nur ein Teil davon. Wenn es jetzt gelingt, nur dieses Editierfeld neu zu zeichnen - und mehr ist auch nicht notwendig - dann hat man einen ungeheuren Gewinn an Performance, denn das Editierfeld bedeckt ja nur einen winzigen Teil des ganzen Fensters.
Wenn das Panel neu gezeichnet werden muss, dann muss ich selbstverständlich auch das Editierfeld neu zeichnen. Das mache ich so: Wenn ein Element "Invalid" wird, setzt es auch alle seine Kindelemente auf Invalid - sonst verschwinden sie nämlich beim nächsten Zeichenvorgang, genau wie bei Deinem Rendern. Mein selektives Rendern funktioniert nur deshalb, weil man davon ausgehen kann, das die Kinder eines GUI-Elements (das TEdit) auf dem GUI-Element (das TPanel) draufsitzt. Es gibt auch Ausnahmen, z.B. die ComboBox. Das ist eine kompliziertere Geschichte.
Ich machs im Augenblick so, dass ich einfach den Viewport nicht lösche, aber das hat auch Nachteile. Tak arbeitet mit glScissor. Es hat alles den gleichen Zweck: möglichst sparsam mit gezeichneten Pixeln umzugehen. Das Editierfeld hat einen Buchstaben empfangen und weiss jetzt daher sicher, dass er neu gezeichnet werden muss, er setzt am Ende der Methode "OnKeyDown" seine Variable "Invalid" auf TRUE. In der Paintmethode wird einfach gefragt, ob er "invalid" ist. Dann wird gezeichnet und seine Variable Invalid wieder auf FALSE gesetzt. Das war's schon, mehr ist nicht dahinter.
Registriert: Do Dez 05, 2002 10:35 Beiträge: 4234 Wohnort: Dortmund
Zitat:
Für mich sieht es jetzt so aus, als ob ich zuviele Boschaften empfange und verarbeite.
Also dafür könnte ich dir eine Testversion eines Tools empfehlen. Das nennt sich AQtime und ist ein Profiler. Der misst wie häufig die einzelnen Methoden aufgerufen wurden und wie viel Zeit sie benötigt haben. Wenn man damit einmal klar kommt kann man sehr schnell sehen wo ein knackpunkt ist. Die Testversion läuft 30 Tage. Der dürfte die auch genau zeigen wie viele Events du bekommen hast bzw wo dann die Zeit verlohren gegangen ist.
Registriert: Mi Jan 31, 2007 18:32 Beiträge: 150
Programmiersprache: Pascal
Warum setzt du (ich gehe mal davon aus das du im Ortho modus zeichnest) glOrtho(left, right, bottom, top, znear, zfar : double); nicht die Clippingplans und vergiebts entlang deiner Hirachie einen Tiefenwert somit müsstest du nicht die ganzen Children auf "Invalid" setzen.
Registriert: Di Okt 03, 2006 14:07 Beiträge: 1277 Wohnort: Wien
Das versteh ich jetzt nicht. Wie soll denn der Tiefenwert wissen, ob das Element neu gezeichnet werden muss? Denn die Entscheidung, ob das Element neu gezeichnet werden muss, hängt davon ab, ob im gegebenen Fall etwas anderes auf dem Bildschirm zu sehen ist als vorher (auf einem kleinen, begrenzten Teil des Bildschirms). Man kann hier nicht in den Kategorien einer 3D Spieleanwendung denken. Eher so, als ob Du ein DrawGrid hättest, wo in jeder Zelle ein Bild drinhängt, und wo bestimmte Bilder zu bestimmten Zeitpunkten durch ein aktuelleres Bild ersetzt werden.
@Lossy: vielen Dank, aber das müsste ich auch ohne ein Zusatzprogramm schaffen. Alle diese Botschaften müssen ja durch die Botschaftsbehandlungsroutinen des TGUIItems durch (dem Ahnen aller GUIItems). Das sind nicht so viele. Und was für ein Item genau das ist, das diese geerbete Botschaftsroutine grade benutzt, kann ich feststellen, denn sie haben alle eine Identität (einen Namen). Zusätzlich ausgerüstet mit einem Logfile müsste ich mit relativ geringem Aufwand feststellen können, was da "abgeht" (im wahrsten Sinne des Wortes ).
Aber etwas Gutes kann ich jedenfalls berichten: Wenn ich mit dem Finger auf einer Taste draufbleibe, benimmt er sich ganz brav genauso wie Windows, hat auch keinen Nachlauf. Das "Nachlaufen" des Textes tritt nur auf, wenn ich mehrere Tasten zugleich drücke.
Registriert: Mi Jan 31, 2007 18:32 Beiträge: 150
Programmiersprache: Pascal
villeicht war das jetzt ein bisschen unverständlich das war so gemeint :
Panel -> Edit
wenn jetzt das Panel neu gezeichnet werden muss müssen nach deinem beispiel alle beide neu gezeichnet werden....
wennn du jetzt aber Tiefenwerte benutzt
Panel(-1) -> Edit(0)
musst du nur das Panel neu zeichnen da es automatisch hinter dem Edit oder anderen children landet
Mitglieder in diesem Forum: 0 Mitglieder und 5 Gäste
Du darfst keine neuen Themen in diesem Forum erstellen. Du darfst keine Antworten zu Themen in diesem Forum erstellen. Du darfst deine Beiträge in diesem Forum nicht ändern. Du darfst deine Beiträge in diesem Forum nicht löschen. Du darfst keine Dateianhänge in diesem Forum erstellen.