Wie am besten text rendern?

Für Fragen zu Grafik APIs wie DirectX und OpenGL sowie Shaderprogrammierung.
Antworten
snoob741
Beiträge: 76
Registriert: 03.04.2012, 22:36

Wie am besten text rendern?

Beitrag von snoob741 »

Hallo zusammen,

ich veruche mich grad an Font-Rendering auf Basis von Freetype. Läuft soeit auch gut und
ich kann problemlos meine Atlastextur mit den zu rendernden Glyphen erstellen. Nun hader
ich z.Z. etwas mit der Umsetzung des Renderings, wie sollte ich das am Besten machen im
Hinblick auf unterschiedliche Systeme (Dx, GL, GLES).

Ein Gedanke war Vertex Buffer (Objects) zu verwenden. Problem das ich hier sehe, ist der
dynamische Text, der zum einen ständig wechseln kann (z.B. bei FPS Raten Anzeige) und
in der Länge vorab nicht bestimmbar ist. Unter GL kein Problem, bei DX gibt es ja dann
schon Probleme bei der Erzeugung des Buffers, da CreateVertexBuffer die Länge verlangt,
die ich vorab ja nicht kenne. Und da das allokieren auf der GPU kostenintensiv ist, wollte
ich nicht ständig neue Buffer generieren ...

Ein anderer Gedanke war, hier einen spezifischen Renderer/Buffer zu schreiben, der die
Systeme unterschiedlich händelt, z.B. in DX DrawPrimitiveUP zum Rendern verwendet,
während GL drawArrays nutzt ...

Wie setzte ich das am Besten um, auch in Hinblick auf spätere Shader Verwendung für GL3
und GLES bzw. DX11?

Bin dankbar für jeden Vorschlag und schlag auf den Hinterkopf falsch ich zu kompliziert
gedacht habe :lol:

Gruss
Snoob
Florian Keßeler
Beiträge: 75
Registriert: 24.07.2002, 00:00
Wohnort: Bremen
Kontaktdaten:

Re: Wie am besten text rendern?

Beitrag von Florian Keßeler »

Selbst wenn sich deine FPS 10x pro Sekunde ändert, würde ich mir da gar keine Gedanken machen. Einfach den neuen Text in die alte Textur rendern und gut. Verglichen mit deinen übrigen 100FPS oder was am Ende bei rauskommt dürfte das gar nicht ins Gewicht fallen. Wenn du dir das schön kapselst, kannst du es aber jederzeit wieder austauschen, sollte es sich wirklich zum Bottleneck entwickeln (was ich aber nicht glaube). Investier deine Zeit lieber erstmal in Features und Funktionalität. Optimieren kannst du immer noch (vor allem, wenn es so ein relativ kleines Subsystem wie die Textanzeige ist, die dürfte kaum geschwindigkeitskritische Architekturauswirkungen auf andere Bereiche haben).
Zuletzt geändert von Florian Keßeler am 19.06.2012, 12:31, insgesamt 1-mal geändert.
Benutzeravatar
Schrompf
Moderator
Beiträge: 4886
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas
Wohnort: Dresden
Kontaktdaten:

Re: Wie am besten text rendern?

Beitrag von Schrompf »

Ich würde eine kleine "Render Das Für Mich"-Klasse basteln, der ich einen Texturatlas und ein Rudel Zeichenbefehle geben kann. Die konstruierst Du dann mit Platz für eine bestimmte Anzahl Zeichen, so dass die Klasse sich ein passendes VertexBufferObject (oder unter DX: ein dynamischer VertexBuffer) erzeugen kann. Und entweder Du schmeißt den Buffer weg und nimmst einen neuen, wenn er zu klein sein sollte, oder Du renderst den bisherigen Inhalt aus und fängst von vorne an zu füllen. Direct3D optimiert mit Sicherheit für solche "mehrmals pro Frame"-VertexBuffer, da brauchst Du Dir keine Sorgen zu machen.

Was ich übrigens empfehlen kann: mach einen allgemeinen Sprite-Renderer draus. Buchstaben sind ja nur spezielle Grafik-Elemente, aber man kann auch für die GUI oder für alle Arten von 2D-Spielen einen allgemeinen Sprite-Renderer gut gebrauchen.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
snoob741
Beiträge: 76
Registriert: 03.04.2012, 22:36

Re: Wie am besten text rendern?

Beitrag von snoob741 »

Bisher render ich das ganze über VBO's, d.h. ich erzeuge Quads entsrpechend der Textvorgaben und mappe dann entsprechend die Glyph texturen aus dem Atlas. Ich hatte darüber nachgedacht eine Rendermethode zu implementieren, die einen Buffer als Parameter erwartet und hier den Text added. Später wird der Buffer, gerendert...

Wenn ich Euch richtig verstanden habe, sollte ich es umgekehrt machen, d.h. nen buffer zurückliefern der neu allokiert wurde und diesen dann später rendern. Ok, sollte leicht zu implementieren sein ...

Was meinst du direkt mit dem Sprite-Renderer Schrompf? Ich hab schon mal Richtung Sprites geschielt, zumal ich später den Text ja z.B. auch auf Partikeln anwenden können möchte. Die Font selbst soll in der Standardgui über widgets eingebunden werden ...
Benutzeravatar
Schrompf
Moderator
Beiträge: 4886
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas
Wohnort: Dresden
Kontaktdaten:

Re: Wie am besten text rendern?

Beitrag von Schrompf »

Ich meinte:

Code: Alles auswählen

class SpriteBatch {
  SomeVertexBuffer buffer;
  std::vector<DrawMe> drawCommands;

  void Draw( const TextureAtlas& tex, size_t elementIndex, float x, float y, Color4D color, float scale, float rotation, ... sonstige Parameter ... );
  void Finish();
};
Extra in Englisch für die Allgemeinheit :-) Was ich damit sagen will: Du erschaffst eine Klasse, die im Konstruktor einen VertexBuffer für eine bestimmte Anzahl Quads reserviert. Davon kannst Du Dir jetzt einen (dauerhaft oder nur für den Moment) anlegen und mit ihm die Quads zeichnen, die Deinen Text ausmachen.

Ich habe in meinem Sprite-Renderer aktuell noch Batch und TextureAtlas kombiniert. Das hat sich aber trotz aller unbestreitbaren Bequemlichkeit hier und da schon als Problem erwiesen. Beispiel: MultiCore-Renderer. Ich könnte bei Splatter einiges an Performance rausholen, indem ich all die Passes, die dafür so nötig sind, parallel abfeuere und einfach nur die DrawCalls in so einem SpriteBatch sammle. Das Befüllen und Ausrendern der VertexBuffer muss dann natürlich seriell passieren.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
snoob741
Beiträge: 76
Registriert: 03.04.2012, 22:36

Re: Wie am besten text rendern?

Beitrag von snoob741 »

Ah ja jetzt ist mir ein Licht aufgegangen. Geniale Idee. Dann muß ich mal schauen was ich daraus stemmen kann!
Dickes Dankeschöön!
Florian Keßeler
Beiträge: 75
Registriert: 24.07.2002, 00:00
Wohnort: Bremen
Kontaktdaten:

Re: Wie am besten text rendern?

Beitrag von Florian Keßeler »

Ich würde eher nicht zeichenweise selber rendern. Dabei kann man zu viel falsch machen, wodurch der Text dann merkwürdig aussieht. Wenn du eh FreeType drin hast, render doch einfach den ganzen Text mit FreeType in eine Textur oder nach dem du mit 3D fertig bist direkt in den Framebuffer. Ich bin nach wie vor der Meinung, dass das bißchen Text, dass man üblicherweise hat keinen spürbaren Performanceeinfluss hat.
Benutzeravatar
Schrompf
Moderator
Beiträge: 4886
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas
Wohnort: Dresden
Kontaktdaten:

Re: Wie am besten text rendern?

Beitrag von Schrompf »

Ich dagegen empfinde es als Verschwendung, für jeden Text eine kleine Textur anzufangen und die zu behalten und alle möglichen Änderungsfälle abzufangen und die Textur neu zu erstellen. Außerdem ist es auch ein merklicher Performance-Verlust, ein Rudel einzelner Texturen zu zeichnen.

Ich stimme Dir allerdings zu, dass man beim Layout von Buchstaben und Zeilen einiges falsch machen kann. Da steckt einiges an Hirn drin, bis ein Text so aussieht wie z.B. dieser Text hier im Browser, in allen Sprachen, mit allen Fonts. Allerdings muss man einen Großteil davon auch nicht nachmachen, es sieht auch so gut aus.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
snoob741
Beiträge: 76
Registriert: 03.04.2012, 22:36

Re: Wie am besten text rendern?

Beitrag von snoob741 »

Da muss ich mich Schrompf anschließen, eine einzelne Textur pro Text sehe ich als zu große Verschwendung an (Klingt ja schon so, als ob man zuviel JAVA getankt hat :twisted: ) besonders im Hinblick auf die mobilen Systeme, wo die Resourcen generell knapp sind. Daher hatte ich ja auch muffen, dass das allokieren der Vertexbuffer zuviel Performance drop erzeugen könnte ...
Benutzeravatar
Schrompf
Moderator
Beiträge: 4886
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas
Wohnort: Dresden
Kontaktdaten:

Re: Wie am besten text rendern?

Beitrag von Schrompf »

Was das permanente Neuerstellen von Resourcen angeht - und ganz ehrlich: dynamisch gelockte VertexBuffer sind nix anderes - bist Du leider in den Händen der Treiber-Hersteller. Nach meiner Erfahrung klappt das aber zumindest auf Windows prächtig. Über mobile Geräte kann ich Dir leider nichts sagen.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
snoob741
Beiträge: 76
Registriert: 03.04.2012, 22:36

Re: Wie am besten text rendern?

Beitrag von snoob741 »

Ich hab mir gestern Abend mal Gedanken gemacht über den von Schrompf angesprochenen SpriteBatcher gemacht. Die Idee gefällt mir immer besser :D Allerdings kamen inzwischen auch einige Fragen für das Handling auf, und da würde ich gern einmal auf Eure Erfahrung zurückgreifen.

Ziel des Batchers ist es ja Sprites, die auf die gleichen Textur zugreifen in einem Pass zu rendern. Soweit so gut. Nur wie geht man am besten damit um, wenn es Sprites mit unterschiedlichen Texturen sind? Haltet Ihr hierfür mehrere SpriteBatcher für das Handling bereit oder nutzt Ihr hier eher einen Systemübergreifenden Batcher, der dies irgendwie handelt, z.b. durch Gruppieren von Sprites, so dass diese verschiedene Buffer allokieren und dann hintereinander weg gerendert werden? OpenGL ermöglicht es ja z.B. relativ einfach in einem Schritt mehrere VBO's zu erzeugen ...

Was die Allokation angeht habe ich darüber nachgedacht, bei der Initialisierung eine "Grundmenge" mit zu geben. z.B. 500 Elemente als Basis. Erst wenn diese überschritten wurde würde ich den Buffer dynamisch neu allokieren, z.B. 25% größer. Ist das Sinnvoll und welche Menge würdet Ihr als Ausgangsrichtwert nehmen?
Benutzeravatar
Schrompf
Moderator
Beiträge: 4886
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas
Wohnort: Dresden
Kontaktdaten:

Re: Wie am besten text rendern?

Beitrag von Schrompf »

Gute Fragen.

Zum Einen: bei mir ist wie gesagt der SpriteBatch noch mit dem TexturAtlas verbunden. Demzufolge ist es bei mir unvermeidlich, dass ein Batcher nur Elemente von einer Textur rendern kann. Ich benutze den Texturatlas aber auch als dynamische Sammlung: man kann jederzeit weitere Grafikteile hinzufügen oder löschen, man kann aber auch den Texturatlas auf Platte speichern oder laden, um das dynamische Zusammenstellen zu vermeiden. Ich gruppiere da thematisch und hatte daher bisher irgendwie nie die Not, von mehreren Texturen Grafikelemente zu mischen.

Die Fonts werden bei uns z.B. dynamisch zusammengestellt - alle nötigen Zeichen werden per freetype ausgerendert, auf dem Texturatlas zusammengefasst und können dann mit einem FontRenderer zum Texten verwendet werden. Das geht solange prima, wie Du nicht mittem im Text die Schriftart ändern willst. Ab dann hätte Florians Ansatz wieder einen Vorteil, da ist das stressfrei möglich, wenn auch vom Layout her immernoch herausfordernd. Wenn Du nur gewisse Parameter wie Schriftgröße, Strichdicke oder Outlining verändern willst, bietet sich z.B. die FontRendering-Technik von Valve über Signed Distance Fields an - das sind dann nur noch Shader-Parameter, die Du locker pro Zeichen variieren kannst. Schriftgröße, Rotation, Kerning kannst Du ja dank GPU-Rendering eh schon stressfrei variieren. Nur der Wechsel von Arial zu Courier würde ein Problem darstellen.

Randnotiz: nach meiner Erfahrung ist ein Texturatlas für einen z.B. 32er Font mit allen mitteleuropäischen Zeichen nicht sonderlich groß - eine 512er, evtl. ein bisschen mehr. Du könntest also auch stressfrei mehrere Fonts auf einem Texturatlas unterbringen. Wenn ich bedenke, dass Splatter aktuell mit einem 4096er Texturatlas für alle Sprites arbeitet, ist da noch ne Menge Luft nach oben.

Und zur Allokation: da bei mir immernoch SpriteBatcher und TexturAtlas verbunden sind, stellt sich das Problem für mich nicht. Die Buffer werden einfach von Frame zu Frame wiederverwendet. Die zu zeichnenden Elemente werden eh erstmal in einem std::vector gesammelt, der bei Bedarf stressfrei mitwächst. Und beim eigentlichen Ausrendern dann fülle ich den VertexBuffer mit sovielen Elementen, wie reinpassen, rendere den aus, und fülle von vorn. Die Größe des Buffers ist per Konstruktor einstellbar, und man weiß ja eigentlich, wieviele Elemente grob zu erwarten sind. Der FontRenderer spuckt ein paar dutzend Elemente pro Text aus, in der GUI insgesamt vielleicht 100. Wenn ein Tooltip aufklappt, sinds auch mal 300. Und die Sprites arbeiten aktuell mit einer Buffergröße von 5000 Elementen - da ich Instancing für das Rendern benutze, passt bei mir ein Sprite in einen 64Byte-Vertex, was selbst 5000 Elemente auf grademal 300kb-Buffer bringt. Für eine GUI kommst Du mit unter 100 davon.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
snoob741
Beiträge: 76
Registriert: 03.04.2012, 22:36

Re: Wie am besten text rendern?

Beitrag von snoob741 »

Ok ich verstehe. Über ähnliches habe ich auch schon nachgedacht gehabt, nämlich, dass ich pro Font einen SpriteBatcher verwende, der dann das Rendering übernimmt. Zumal ich schon die möglichkeit eingebaut habe, dass ich bei einer Font die ich mit Freetype generiere, auf ein und die selbe Atlas Textur zurückgreifen kann wenn ich verschiedene Sizes generiere ...

Mit den Signed Distance Fields hast du mich ja heiß gemacht, sieht ja echt nett aus 8-) Da muss ich auch noch mal drüber nachdenken. Nochmals ein dickes Dankeschöööön!
Benutzeravatar
Krishty
Establishment
Beiträge: 8268
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Wie am besten text rendern?

Beitrag von Krishty »

Ich bin übrigens bei Text, der sich laufen ändert (FPS-Anzeigen, Statistiken, …) gegen die Wiederverwendung und für Shoot and Forget in Form von Draw Calls mit User Pointern. Gebencht habe ich da aber nichts; allein die Vereinfachung auf Anwendungsseite macht es für mich zur ersten Wahl.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
ftb
Beiträge: 6
Registriert: 13.06.2012, 00:30

Re: Wie am besten text rendern?

Beitrag von ftb »

Mein SpriteRenderer packt alles solange in einen Buffer bis sich die Textur ändert. D.h. wenn sich die Textur resultiert das in einem DrawCall. Problematik dabei ist natürlich, dass man erstmal die Textur irgendwie unterscheiden muss und dass der User selber kucken muss, dass er das ganze selber gruppiert was eventuell nicht so rasend ist. ALternativ könnte man auch eine Liste anlegen und den Buffer erst am Schluss füllen und so für mehr User Freundlichkeit sorgen. Die Frage ist ob es einen nennens werten Unterschied macht.
Ich muss aber zugeben, dass ich den Renderer noch nicht exzessiv praxis getestet habe, deswegen kann das Design definitiv nachteile haben ohne Frage. Wollte hier nur mal meinen Weg aufzeigen (:
Benutzeravatar
Schrompf
Moderator
Beiträge: 4886
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas
Wohnort: Dresden
Kontaktdaten:

Re: Wie am besten text rendern?

Beitrag von Schrompf »

Ich wäre auch vorsichtig mit solchen Basteleien. Ich habe nämlich festgestellt, dass für viele Zwecke die Reihenfolge der Zeichenaktionen wichtig ist. Und sobald man anfängt, Zeichenaktionen verschiedener Texturen zu gruppieren, um Performance rauszuholen, versaut man praktisch zwangsweise die Reihenfolge.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
snoob741
Beiträge: 76
Registriert: 03.04.2012, 22:36

Re: Wie am besten text rendern?

Beitrag von snoob741 »

So habe mal mit der Basisimplementation des SpriteRenderers begonnen. Klappt soweit auch ganz gut. Nur habe ich nach wie vor grad nen logischen Hänger mit der Abarbeitung. Im Prizip arbeitet der Renderer wie ein Stack, d.h. ich pusche munter meine Sprites die gerendert werden sollen per draw clall, lege die Geometriedaten für das Rendern in einer Liste ab und wenn ich Render wird die Liste auf den Buffer übertragen. Überschreitet die Liste die allokierten Buffergröße wird dieser vorher neu initialisiert ...

Dies impliziert aber das ich permanent die Drawcalls aufrufe und den Stack pro frame lösche... Somit muss die Geometrie für jedes Sprite pro Frame neu berechnet werden.

Oder arbeitet Ihr eher wie ein Cache, d.h. einmalig draw aufrufen und dann nur auf die GPU flushen wenn sich was an der Liste geändert hat? Dann müsste ich aber etwas einbauen, was dynamische Daten von statischen unterscheidet, z.B. zwei separate Listen und ein draw call mit Zuordnungsflag. Nur die dynamische Liste würde dann gelöscht werden, die statische nie.

Denke der erste Ansatz ist wahrscheinlich eher state of the art, doch spart man nicht ne Menge durch das Caching? Da z.b. ganze Schriffzüge nicht ständig neu berechnet werden? Oder trennt Ihr sogar einen "normalen" SpriteRenderer und FontRenderer? Wahrscheinlich denke ich schon wieder zu komplizert ...
Benutzeravatar
Schrompf
Moderator
Beiträge: 4886
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas
Wohnort: Dresden
Kontaktdaten:

Re: Wie am besten text rendern?

Beitrag von Schrompf »

Das "Neuberechnen" der Sprites/Quads/Glyphen/Zeichen/WasAuchImmer kann ein bisschen Rechenzeit kosten, aber jetzt nicht exorbitant viel. Besonders seit ich die ganze Quad-Erstellung/Rotation/Skalierung/Texturkantenkorrektur per Instancing in den VertexShader geschoben habe, ist das so billig, dass es nicht mehr von Belang ist.

Du könntest natürlich einen "Cache" implementieren, so im Sinne von "vergleiche die reinkommenden Rendercalls mit dem bestehenden Satz Sprites vom letzten Mal, und setze erst dann ein Neu-Hochladen-Flag, wenn sich der erste Rendercall unterscheidet". Geht. Ob's was bringt, weiß ich aber nicht.

Abgesehen davon gibt da ja noch das Layout der Texte. Also so Sachen wie Zeilenumbruch, Zentrierung, Auspunkten bei Überlänge und sowas. Das mache ich aktuell jedes Frame live bei der Textausgabe, aber ich kann mir vorstellen, dass man ein paar Mikrosekunden sparen kann, wenn man es nur bei Änderungen einmalig tut. Das würde ich dann aber wieder in ne eigene Klasse auslagern. Vielleicht so:

Code: Alles auswählen

class TextLayout {
  void SetText( const std::string& text);
  void SetParams( Font& font, Vector2f screensize, Alignment horizontal, Alignment vertical, and so on);
  void Render( Vector2f topleft, AlphaColor color) const;
};
Dann hast Du eine Klasse, deren Job es ist, einen Text nach gegebenen Parametern anzuordnen und sich das zu merken. Diese Klasse kann dann auf Zuruf den vorformatierten Text irgendwo hin rendern, solange alle Parameter konstant sind. Ich würde aber ernsthaft erstmal messen, ob das überhaupt die Arbeit wert ist.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
snoob741
Beiträge: 76
Registriert: 03.04.2012, 22:36

Re: Wie am besten text rendern?

Beitrag von snoob741 »

Klingt logisch, ich denke ich sollte zuerst einmal alles ausimplementieren. Dann testen und dann kann ich ja immer noch optimieren und evtl. auch ein Chaching einbauen. So viel Text sollte man ja wirklich i.d.R. kaum auf einen Schlag anzeigen... Danke für die Anregungen.
Antworten