1 Punkte von GN⁺ 2025-06-14 | 1 Kommentare | Auf WhatsApp teilen
  • Das bisherige Multi-Channel-SDF-Verfahren hatte weiter Grenzen bei dünnen Strichen, großen Atlanten, Verkleinerung/Vergrößerung und weicher Bewegung, deshalb wurde es stattdessen als GPU-Runtime-Rasterisierung neu umgesetzt.
  • Die neue Pipeline sendet nur die Bézier-Kurvendaten der aktuell sichtbaren Glyphen an die GPU, zeichnet sie zur Laufzeit in einen Atlas und sampelt sie dann auf dem Bildschirm.
  • Solange eine Glyphe auf dem Bildschirm bleibt, wird der Atlas-Cache beibehalten und es werden Samples akkumuliert, um die Qualität zu erhöhen; gestartet wird im ersten Frame mit 8 Samples pro Pixel, verfeinert wird bis zu 512 Samples.
  • Der Cache-Key enthält font, glyph_index, Pixelgröße und Subpixel-Offset; durch 8-Bit-Festkommawerte werden nahe Positionen und Größen auf denselben Wert abgebildet, um die Wiederverwendbarkeit zu erhöhen.
  • Wenn die RGB-Subpixel-Struktur pro Monitor als Sample-Bereich modelliert wird, lässt sich selbst bei nicht standardmäßigen Anordnungen wie dem OLED G9 Farbsäumen verringern, und eine 4K-Vollbild-Textdemo zeigt auf einer Radeon 9070 Spitzenkosten von etwa 0,1 ms.

Warum Runtime-Vektor-Rasterisierung statt SDF

  • Die bisherige Implementierung nutzte Multi-Channel Signed Distance Fields und funktionierte im Großen und Ganzen gut, hatte in der Praxis aber noch einige Einschränkungen.
    • Qualität: Bei dünnen Strichen oder detailreichen Fonts gingen Merkmale verloren oder es entstanden Artefakte, sodass teils höher aufgelöste SDFs nötig waren.
    • Atlas-Größe: Da SDFs offline erzeugt und dann im Atlas gespeichert werden, lassen sich japanische und chinesische Fonts mit vielen Glyphen schwer in einen einzelnen Atlas backen.
    • Flexibilität: Neue Ideen wie Probleme bei Verkleinerung/Vergrößerung oder Subpixel-Antialiasing sind schwer einzubauen, und für zur Laufzeit erzeugte oder bearbeitbare Vektorgrafiken passte es ebenfalls nicht gut.
    • Einfachheit: Der Schritt, originale Glyphen-Kurven in eine Zwischentextur umzuwandeln, erhöht die Systemkomplexität.
  • Selbst ein lateinischer Font wie Miama benötigte mit 64×64 Bereichen pro Glyphe einen 4096×1152-Atlas, und wenn mehrere Fonts zur Laufzeit genutzt werden, steigen Speicherbedarf und Streaming-Bandbreitenkosten deutlich.
  • Ziel des neuen Ansatzes ist es, die von den Glyphen-Designern erstellten Bézier-Kurven direkter zu verwenden und die Zahl der Transformationsschritte von den Originaldaten bis zum finalen Pixel zu verringern.

Die gesamte Pipeline

  • Der Kern ist, nicht alle Glyphen offline vorab zu backen, sondern nur die Kurven der aktuell sichtbaren Glyphen an die GPU zu senden und sie genau dann zu rasterisieren, wenn sie gebraucht werden.
  • Der Ablauf ist einfach.
    • Kurvendaten der Glyphen aus dem Font laden
    • Die GPU rasterisiert die Glyphen zur Laufzeit in einen Atlas
    • Bei der Bildausgabe wird dieser Atlas gesampelt
  • Wenn dieselbe Glyphe auch in späteren Frames weiterverwendet wird, bleibt sie im Atlas und es werden Samples akkumuliert, um sogar hochwertiges Subpixel-Antialiasing weiter zu verfeinern.
  • Weil die Vektorrepräsentation direkt gerendert wird, ist die Anpassung an Auflösungsänderungen einfach, und es lässt sich nicht nur die gesamte Pixelabdeckung, sondern auch die Abdeckung jedes einzelnen Subpixel-Elements berechnen.

Verarbeitung der Glyphen-Kurven

  • Zum Laden der Fonts wird FreeType als Zwischenschicht eines Offline-Tools verwendet, um unterstützte Formate zu lesen, die Kurven jeder Glyphe zu durchlaufen und sie im eigenen Asset-Format zu speichern.
  • Glyphen-Kurven können Linien, quadratische Bézier-Kurven und kubische Bézier-Kurven enthalten; um die Shader einfach zu halten, werden alle in quadratische Bézier-Kurven umgewandelt.
    • Linien werden durch Hinzufügen eines Kontrollpunkts in der Mitte zwischen zwei Punkten zu quadratischen Bézier-Kurven.
    • Kubische Bézier-Kurven werden in zwei quadratische Bézier-Kurven aufgeteilt; das ist eine verlustbehaftete Gradreduktion.
  • Diese Aufteilung kubischer Bézier-Kurven in zwei quadratische funktionierte bei den meisten getesteten Fonts gut, es sind aber auch ausgefeiltere Verfahren möglich, um den Fehler weiter zu reduzieren.
  • Wenn eine höherwertige Umwandlung nötig ist, lässt sich das im Offline-Tool vermeiden, indem in ein Format mit nur quadratischen Bézier-Kurven wie TrueType .ttf konvertiert wird.
  • Im Desmos-Diagramm lässt sich die Eingabe-Kubik-Bézier-Kurve mit den zwei resultierenden quadratischen Bézier-Kurven vergleichen.

Abdeckungsberechnung und Optimierung des Kurvenzugriffs

  • Die Abdeckung wird pixelweise berechnet, indem ein horizontaler Strahl von links nach rechts geschickt, die Schnittpunkte mit den Kurven geprüft und die winding number akkumuliert wird.
  • Zum mathematischen Hintergrund und zur Implementierung siehe GreenLightnings GPU Font Rendering und Sebastian Lagues Video Rendering Text.
  • Ungenauigkeiten bei der Schnittberechnung können bei Samples auf bestimmten Höhen auftreten, aber wenn Hunderte Samples akkumuliert werden, fallen ein oder zwei Fehler nach dem Mittelwertbilden kaum noch auf.
    • Wenn bei maximal 512 akkumulierten Samples ein Sample falsch ist, wird aus 0 stattdessen 1/512=0.00195 oder aus 1 stattdessen 511/512=0.99804.
    • Es lassen sich auch Schwellenwerte zum Clamping setzen, wenn die Abdeckung nahe an Extremwerten liegt.
  • Für die Verteilung der Sample-Positionen wird Martin Roberts’ $R_2$ sequence verwendet; die Verteilung über die Zeit ist im Shadertoy-Beispiel zu sehen.
  • Um die Kosten des Kurvenzugriffs zu senken, werden Glyphen in mehrere horizontale Bänder aufgeteilt, und nur die Kurven, die ein Band berühren, werden darin als Bitset gespeichert.
    • Da nur horizontale Strahlen verfolgt werden, wird die Menge der pro Pixel zu prüfenden Kurven stark reduziert.
    • Wenn auf Wave-Ebene auf denselben Bandbereich zugegriffen wird, lassen sich Kurveniteration und Lesezugriffe skalär machen.
    • Beim Rasterisieren in den Atlas im Compute-Shader werden Threads row-major horizontal gepackt, damit der von einer Wave berührte Bandbereich minimiert wird.

Atlas-Packing und Cache-Key

  • Anfangs wurde direkt auf den Bildschirm rasterisiert, aber die Kosten für hochwertiges Antialiasing in jedem Frame waren hoch.
  • Da die meiste Schrift über mehrere Frames hinweg in derselben Größe und Position bleibt und dieselbe Glyphe in derselben Größe wiederholt vorkommt, eignen sich Atlas und zeitliche Akkumulation gut.
  • Wenn eine benötigte Glyphe im Atlas fehlt, wird Platz zugewiesen und die Rasterisierung gestartet; ist sie schon vorhanden, wird das bestehende Ergebnis unverändert genutzt.
    • Während eines Frames werden die Glyphen im Atlas geprüft, um zu entscheiden, ob sie beibehalten, weiter gesampelt oder bei Nichtverwendung wieder freigegeben werden.
  • Der Atlas-Key enthält die folgenden Elemente.
    • font
    • glyph_index
    • quantized_size_in_pixels_x, quantized_size_in_pixels_y
    • quantized_subpixel_offset_x, quantized_subpixel_offset_y
  • Der Subpixel-Offset entspricht frac(pixel_position) und ist nötig, um passende Antialiasing-Ergebnisse zu erzeugen, wenn eine Glyphe nicht exakt auf dem Pixelraster liegt oder weich gescrollt wird.
  • Wenn Fließkommawerte direkt als Key verwendet werden, können Werte, die mathematisch gleich sein sollten, bitweise unterschiedlich sein; deshalb werden nahe Positionen und Größen mit 8-Bit-Festkommawerten auf denselben Wert abgebildet.
  • In einem Texteditor mit viel statischem Text und Monospace-Font lässt sich die Atlas-Cache-Trefferquote derselben Glyphe erhöhen, indem Zeichenabstände und Zeilenpositionen an Pixelgrenzen ausgerichtet werden.

Z-Order-basierte Atlas-Platzierung

  • Für die Laufzeit-Platzierung der Glyphen werden Z-Order Packing und ein Bitset freier Zellen verwendet.
  • Der auf Morton-Code basierende Z-Order-Ansatz behandelt 2D-Zellen wie ein langes 1D-Array; werden eine Anzahl zusammenhängender Zellen in Zweierpotenzen allokiert, ergibt das im 2D-Atlas einen quadratischen Bereich.
  • Die Grundzelle ist 16×16 Texel groß, und Glyphengrößen werden auf die nächste Zweierpotenz aufgerundet.
    • Eine 25×29-Glyphe erhält zum Beispiel einen 32×32-Block.
    • Dafür werden 4 Zellen à 16×16 benötigt, also wird nach 4 ausgerichteten zusammenhängenden Bits gesucht.
  • Lange, schmale Glyphen des lateinischen Alphabets sind oft vertikal ausgerichtet; mit transponierter Z-Order brauchen Glyphen wie l, j, i und 1 nur halb so viel Platz.
  • Bei horizontal langen, schmalen Glyphen wie im Arabischen ist dagegen die standardmäßige Z-Order besser geeignet.

Qualitätssteigerung durch zeitliche Akkumulation

  • Wenn eine Glyphe im Atlas bleibt, können in jedem Frame schrittweise weitere Samples hinzugefügt werden, um das Ergebnis zu verfeinern.
  • Das Standardschema ist: Im ersten Frame, in dem die Glyphe erscheint, 8 Samples pro Pixel, im nächsten 4, dann 2 und danach pro Frame 1 zusätzliches Sample, bis insgesamt 512 akkumuliert sind.
  • Die hohe Anfangsqualität ist wichtig, weil sich sanft bewegende oder in der Größe veränderte Glyphen so verhalten, als würden sie in jedem Frame neu initialisiert.
  • Qualität und Performance lassen sich auf verschiedene Weise abstimmen.
    • Anzahl zusätzlicher Samples/Strahlen pro Frame
    • Ob in den ersten Frames einer Glyphe die Samples erhöht werden
    • Obergrenze für die Gesamtzahl Samples pro Frame
    • Time-Slicing, bei dem bestehende Glyphen nicht in jedem Frame, sondern nur alle paar Frames aktualisiert werden
    • Begrenzung der Schnittprüfungskosten abhängig von der Zahl der Glyphen-Kurven
  • In dieser Implementierung war Performance kein großes Problem; der eingangs gezeigte Vollbildtext kam auf einer Radeon 9070 bei 4K auf etwa 0,1 ms Spitze, und sobald Glyphen die maximale Sample-Zahl erreichen, fällt der Aufwand schnell gegen 0.

Subpixel-Antialiasing und Farbsäume

  • Beim Subpixel-Antialiasing werden die roten, grünen und blauen Elemente innerhalb eines Monitorpixels jeweils als eigene Sample-Bereiche gerendert.
  • Bei der klassischen vertikalen RGB-Streifenstruktur eines LCD verdreifacht sich die horizontale Auflösung faktisch, sodass 3840×2160 bei 4K wie 3840×6480 behandelt werden kann.
  • Ein Problem ist, dass Monitore wie das OLED G9 eine nicht standardmäßige Subpixel-Struktur besitzen, die von den üblichen vertikalen RGB-Streifen abweicht.
    • Wenn eine einfache vertikale rechteckige RGB-Struktur angenommen wird, erscheinen oben grüne und unten magentafarbene Farbsäume.
    • Werden die Sample-Positionen an die tatsächliche Subpixel-Struktur des Monitors angepasst, verschwinden Farbsäume fast vollständig und das Ergebnis wirkt weich.
  • Subpixel Zoo zeigt Beispiele verschiedener Subpixel-Strukturen, und LG WOLEDs red-white-blue-green-Struktur ist ein Beispiel für eine vom Standard abweichende Reihenfolge.
  • Wenn die Subpixel-Elemente größer als ihre tatsächliche physische Größe betrachtet und so gesetzt werden, dass sie sich überlappen, ergaben sich präzisere Resultate.
    • Das Licht der Subpixel mischt sich natürlicherweise und streut leicht, sodass sich die Sample-Bereiche so verhalten, als wären sie größer als die physischen Subpixel.
    • Die Sample-Bereiche sollten auch über das Pixel hinausreichen und sich mit dem Subpixel-Licht benachbarter Pixel mischen.
  • Evan Wallaces Easy Scalable Text Rendering argumentiert, dass nach dem Subpixel-Antialiasing ein horizontaler Blur nötig ist; das hat praktisch denselben Effekt wie eine größere, überlappende Modellierung der Subpixel-Elemente.

Software sollte Subpixel-Informationen des Displays kennen können

  • Wenn sich auf beliebige Subpixel-Strukturen eines Monitors zugreifen ließe, könnten allgemeines Subpixel-Antialiasing und die Text-Rendering-Qualität verbessert werden.
  • Würden solche Informationen über gemeinsame Display-Protokolle bereitgestellt, wäre selbst bei Monitoren mit Standardanordnung feinere hardwareabhängige Darstellung möglich.
  • Display-Hersteller müssten wegen Problemen beim Text-Rendering nicht darauf verzichten, mit besseren Subpixel-Strukturen zu experimentieren.
  • Samsung hat bei QD-OLED vom G8 zum G9 die Subpixel-Struktur geändert, um solche Probleme zu reduzieren, und bei LG WOLED sowie Samsung QD-OLED werden Farbsäume häufig thematisiert.
  • Dieses Problem bietet Spielraum für Software-Korrektur statt nur für Hardware-Austausch.

Der praktische Wert von Echtzeit-Glyphen-Rendering

  • Gute UI- und Textqualität kann die wahrgenommene Qualität eines Produkts erhöhen.
  • In Spielen gibt es oft Elemente wie Textboxen, Menüs, Titel und Benachrichtigungen, auf die der Blick der Nutzer fällt; schlechte Textqualität kann das Erlebnis genauso beeinträchtigen wie schlecht gerenderte 3D-Szenen.
  • Die Persona-Reihe, Metaphor: ReFantazio und Nier: Automata sind gute Beispiele für UI- und Textdarstellung.
  • Versuche, die Qualität des Echtzeit-Glyphen-Renderings zu verbessern, haben sowohl für UI als auch für Game Rendering praktischen Wert.

1 Kommentare

 
GN⁺ 2025-06-14
Meinungen auf Hacker News
  • Was ist im ersten Video mit dem Punkt des kursiven j passiert?

  • Subpixel-Font-Rendering ist wichtig für die Lesbarkeit, aber wie der Artikel anmerkt, ist es bedauerlich, dass man aus bestehenden Display-Standards keine Spezifikation der Pixelanordnung bekommen kann

    • Das gilt nur für Displays mit Standardauflösung, und selbst dort ist es eher nett zu haben als „unverzichtbar“
      Die Welt ist zunehmend zu Retina-Displays übergegangen, und dort gibt es kaum noch einen Grund für Subpixel-Rendering
      Es bringt viele Probleme mit sich: Screenshots sind an eine bestimmte Subpixel-Anordnung gebunden, und auch das Skalieren von Bitmaps ist schwierig
      Es war eine vorübergehende Innovation der LCD-Ära zwischen CRT und Retina und ist heute eher eine rückwärtsgewandte Technik. Es gibt gute Gründe, warum Apple sie vor einigen Jahren aus macOS entfernt hat
    • Der DisplayID-Standard, also der moderne Nachfolger von EDID, scheint laut https://en.wikipedia.org/wiki/DisplayID#0x0C_Display_device_... zumindest darauf ausgelegt zu sein, das zu ermöglichen
      Ich frage mich, ob Display-Hersteller es einfach nicht implementieren. Jedenfalls sind das Informationen, die sich für die gängigsten Display-Modelle leicht ableiten und in einer Hardware-Informationsdatenbank speichern ließen
    • Ich verstehe nicht, warum das immer noch nicht geht. Das hätte doch schon seit Jahrzehnten möglich sein müssen
      Der Artikel ist hervorragend und verlinkt auch einen „subpixel zoo“ mit vielen Beispielen: https://geometrian.com/resources/subpixelzoo/
    • „Tragödie“ ist etwas übertrieben. Jedes Betriebssystem müsste nur eine Entsprechung zum früheren ClearType-Tuner von Windows anbieten und das Ergebnis pro Bildschirm oder Monitormodell merken
      Selbst in den unvermeidlichen Fällen, in denen ein Monitor eine falsche Anordnung meldet, braucht man so einen Ansatz
    • Subpixel-Rendering ist für die meisten Sprachen nicht nötig
      Auch Bitmap-Schriften ohne Antialiasing oder gehintete Vektorfonts bieten schon sehr gute Lesbarkeit
      Wichtig wird es nur bei Sprachen wie Chinesisch oder Japanisch, deren Schriftzeichen sehr komplexe Details haben
  • GTK4 hat beim Verlagern des Renderings auf die GPU RGB-Subpixel-Rendering aufgegeben
    Ich hatte gehört, dass GPU-zentrierte Entscheidungen es schwierig machten, RGB-Subpixel-Rendering beizubehalten, aber der Artikel zeigt, dass es möglich ist
    Dann lagen die Gründe bei GTK vielleicht woanders, oder die vorgeschlagene Lösung hat Nachteile, oder sie lässt sich nicht gut in den bestehenden Stack integrieren

    • Cosmic Text (Cosmic DE) kann das über swash möglicherweise auf der GPU tun. Es unterstützt Subpixel-Rendering
  • Wenn dich interessiert, wie man SDF und MSDF in WebGL / WebGPU implementiert, kannst du dir mein eigenes Tutorial ansehen: https://infinitecanvas.cc/guide/lesson-015#msdf

    • Sieht gut aus. Ich interessiere mich für WGPU, die WebGPU-Implementierung in Rust, und auch wenn dieses Tutorial sich selbst nicht so bewirbt, wirkt es im Grunde wie ein fortgeschrittener Kurs
      Ich habe schon einmal JavaScript-Beispiele nach Rust übertragen; man kann nicht einfach kopieren und einfügen, aber die APIs sind ähnlich genug, dass das Portieren leichtfällt, was ideal zum Lernen ist
      Es ist auch ein guter Anlass, sich mit der WGPU-Dokumentation vertraut zu machen
    • Das Format der Website ist wirklich gut
      Ich erstelle gern GPU-bezogene Tutorials und würde sie gern so strukturieren; ich frage mich, ob das eine vorhandene Vorlage ist oder Teil irgendeines Kurses
  • Die Slug-Bibliothek ist kommerzielle Middleware, die so einen GPU-Glyph-Rasterizer implementiert
    [1]: https://sluglibrary.com/

    • Auf der Website wird der Algorithmus ziemlich ausführlich erklärt; ich frage mich, ob es darauf Patente gibt
      Es wäre interessant, mit Teilen von Font-Parsing und Layout aus cosmic-text eine Open-Source-Version für wgpu zu bauen, aber wenn man am Ende von Slug verklagt wird, wäre das überhaupt nicht mehr lustig
  • Ich verstehe immer noch nicht, warum man Text offline rendern, in einem Atlas speichern und sogar Tricks wie SDF verwenden muss, wo GPUs doch praktisch unbegrenzt viele Vertices/Pixel zeichnen zu können scheinen
    Auch der Artikel sagt, dass Glyph-Kurven in einen Atlas geschrieben werden; ich frage mich, warum ein Shader den Text nicht direkt rendern kann
    Es muss doch sicher eine Möglichkeit geben, Béziers in Dreiecksmeshes umzuwandeln. Ich fange gerade an, einen GPU-Text-Renderer für eine CAD-App zu bauen, und hoffe, den Grund bald zu verstehen

    • Wenn dieselbe Glyphe wiederholt gerendert wird, ist es meistens günstiger, das Ergebnis zu cachen
      GPUs sind schnell, aber nicht unendlich schnell, und sie sind sehr gut darin, vorgerenderte Texturen zu sampeln
      Es geht nicht nur um Geschwindigkeit, sondern auch um Energieverbrauch. Wenn man die Bildwiederholrate des Monitors bereits erreicht, erhöht zusätzliche Performance nicht die Reaktionsfähigkeit, kann aber die Akkulaufzeit verlängern
      Beim Rendering gibt es kein „schnell genug“; schneller zu werden bringt immer einen Vorteil
    • Selbst einfache Fonts haben bei typischen Anzeigegrößen eine enorme Dreiecksdichte
      Moderne GPU-Architekturen sind alle schlecht darin, sehr dichte Geometrie zu verarbeiten. In solchen Fällen ist es deutlich ineffizienter, einfach Dreiecke an die GPU zu schicken, als einen Atlas oder andere Techniken zu nutzen
      Die meisten GPUs dispatchen Pixel-Shader in Vierergruppen. Wenn alle Dreiecke nur 1 Pixel groß sind, tragen drei Shader-Threads nichts zur sichtbaren Ausgabe bei
      Das nennt man Quad-Overdraw. Außerdem verbringt man ohne echten Grund viel Zeit mit Vertex-Verarbeitung
    • GPUs haben keine unbegrenzte Fähigkeit, Vertices/Pixel zu zeichnen. Text direkt zu rendern ist schlicht teurer
      Es ist möglich, aber man gibt ohne großen Nutzen einen Teil des Frame-Budgets auf und erhöht den Stromverbrauch
    • Dreiecke sind die falsche Wahl, aber die grundsätzliche Frage geht in die richtige Richtung
      Der Autor verwendet einen Atlas, weil er Bézier-Kurven mit bis zu 512 Samples pro Pixel supersampelt, und das ist sehr teuer
      Wenn man stattdessen das Schnittintegral zwischen dem Bereich der Bézier-Kurve und dem Subpixel-Bereich berechnet, könnte das viel schneller sein, in Echtzeit ohne Atlas laufen und vermutlich genauer als Supersampling sein
    • GPUs sind sehr schnell, aber nicht unendlich. Wenn man GPU-Zeit für Text ausgibt, kann man sie nicht anderswo einsetzen
      Und fast immer möchte man diese Zeit lieber anderswo nutzen
      Je mehr GPU-Zeit benötigt wird, desto schneller muss auch die Mindesthardware werden. Text ist schön und wichtig, aber vielleicht nicht so wichtig, dass man dafür Nutzer oder Kunden verliert
  • Das wirkt schlimmer als nur „neue OLEDs sehen zwar gut aus, haben aber wegen ihrer nicht standardisierten Subpixel-Struktur Probleme mit Farbsäumen“
    Soweit ich es verstanden habe, ist es nicht einfach nur nicht standardisiert, sondern bei OLED gibt es mehrere Subpixel-Anordnungen, die nicht miteinander kompatibel sind
    Deshalb hat FreeType kein Subpixel-Rendering für OLED implementiert, und wenn man mit Text arbeitet, ist das ein Grund, OLED zu meiden
    Es ist auch nicht nur ein FreeType-Problem; GUI-Toolkits wie Qt und GTK müssten ebenfalls mitziehen. Ich weiß nicht, ob es bei der Lösung Fortschritte gibt
    Es wäre gut, auf die Subpixel-Struktur eines beliebigen Monitors zugreifen zu können, und vielleicht sollten solche Informationen per EDID übermittelt werden

    • Es gibt auch OLEDs mit einigermaßen standardmäßigen Subpixel-Anordnungen
      Mein Laptop hat zum Beispiel eine vertikale BGR-Anordnung, die von FreeType und KDE gut unterstützt wird
      Seltsame Anordnungen scheinen oft daher zu kommen, dass HDR-Displays je nach Farbe unterschiedliche Größen verwenden müssen, damit bestimmte Farben, vor allem Blau, nicht zu schnell einbrennen
    • Theoretisch stimmt das, aber in der Praxis schreibe ich Code auf einem 4K-OLED-Display und habe keinerlei Artefakte bemerkt
  • Sehr beeindruckende Arbeit
    Für alle, die mit diesem Bereich nicht vertraut sind: Valve hat SDF-Text-Rendering für Spiele entwickelt und 2007 ein wegweisendes Paper zu diesem Thema veröffentlicht
    Diese Technik wird bis heute in Videospielen sehr breit und fast unverändert eingesetzt
    2012 entwickelte Behdad Esfahbod mit Glyphy eine SDF-Implementierung, die per OpenGL ES auf der GPU läuft; sie wurde wegen ihrer Performance und neuer Funktionen wie schneller Texttransformationen breit anerkannt, aber nicht weithin eingesetzt
    Moderne Betriebssysteme und Webbrowser bevorzugen stattdessen weiterhin TrueType-Rasterisierung im Stil der 1990er Jahre
    Das ist ein leichtgewichtiger und effektiver Ansatz, aber wie im Artikel zu sehen ist, ermöglicht er weder Subpixel-Ausrichtung noch beliebige Subpixel-Anordnungen; Zoomen ist mit hohen Performance-Kosten verbunden, und komplexe Transformationen wie Neigen, Drehen oder 3D-Transformationen lassen sich nicht innerhalb der Text-Rendering-Engine durchführen
    Wenn man gedrehten oder transformierten Text braucht, muss man ein Bitmap resamplen, was all die kleinen Merkmale zerstört, die Lesbarkeit erzeugen, und entsprechend unschön aussieht
    Der Grund für den langsamen Fortschritt könnte sein, dass Aufwand und Risiko im Verhältnis zum Nutzen zu groß sind. Man stelle sich vor, eine moderne Webbrowser-Engine für GPU-beschleunigtes Text-Rendering neu zu schreiben – das ist keine Kleinigkeit
    Glyph-Rendering ist nur ein Teil davon, Zeilenumbruch ist ein anderes Problem. Es könnte viel Kommunikation zwischen CPU und GPU erfordern und dadurch langsam sein; außerdem ist eine tiefe Integration von Software und GPU schwierig

    • Ich verstehe nicht ganz, warum das so gesagt wird, denn der Teil mit Text-Shaping, Layout und Zeilenumbruch ist fast vollständig unabhängig vom Rendering
    • https://github.com/servo/pathfinder erledigt diese Arbeit mit GPU-Compute-Shadern
      Dieser Ansatz ist deutlich performanter, als ihn wie SDF in die Hardware-3D-Rendering-Pipeline zu zwängen
    • Der Vollständigkeit halber: Text-Rendering einschließlich Subpixel-Antialiasing ist unter Windows seit Langem GPU-beschleunigt und in Chrome/Firefox ebenfalls seit Langem GPU-beschleunigt
      Safari vermutlich auch, aber das kann ich nicht aus eigener Prüfung sagen
      Die Vorstellung, dass der Stand der Technik oder die an Nutzer ausgelieferten Implementierungen sich nicht weiterentwickelt hätten, ist falsch
    • SDF ist kein Allheilmittel
      SDF kodiert die lokale Distanz (Distance) von einem gegebenen Pixel zur Kante eines Zeichens als Feld (Field), also als zweidimensionales Array von Daten, und markiert mit einem Vorzeichenbit (Sign), ob diese Distanz innerhalb oder außerhalb des Zeichens liegt
      Jedes Zeichen hat eine kleine Daten-Map, die in einer GPU-freundlichen Bilddatei zusammen gepackt wird; zusammen mit einer Beschreibungsdatei, die angibt, wo das Teilbild jedes Zeichens zu finden ist, wird sie von einem SDF-Rendering-Shader verwendet
      Solche Zeichendefinitionen sind sehr robust gegenüber linearer Interpolation zwischen Feldwerten, sodass selbst mit relativ niedrig aufgelösten Maps nahezu perfektes Vergrößern möglich ist. GPUs sind außerdem gut darin, Pixelwerte von Maps zu interpolieren
      Wichtig ist aber, dass diese Maps während der Entwicklung für jedes Zeichen, das man aus dem bestehenden Schriftsystem rendern will, vorverarbeitet werden müssen. Das ist für jedes Zeichen nötig, das die Schrift unterstützt
      Das sind viel weniger Daten, als wenn man alle Zeichen als hochauflösende Bitmap-Schrift rendert, aber deutlich mehr als die Glyph-Outline-Definitionen selbst
      Systeme wie Betriebssysteme oder Browser, die jeden möglichen Text weltweit unterstützen wollen, können SDF nicht als Text-Rendering-System verwenden. Man bräuchte SDF-Maps für den gesamten Unicode-Zeichensatz, was viel zu groß würde
      Für Spiele passt es gut, weil sie meist damit auskommen, nicht vollständig lokalisiert zu sein oder keinen völlig beliebigen Text anzeigen zu müssen
      Ursprüngliches SDF kann auch keine Emojis unterstützen, weil es nur die Entfernung zur Glyph-Kante kodiert und keine Farbinformationen innerhalb des Glyphen enthält
      Es gibt zwar eine verbesserte Variante namens Multichannel SDF, die mehrere Farben unterstützt, aber die Gesamtzahl der Farben ist begrenzt
      Wenn man sich Spiele genauer ansieht, die SDF für Ingame-Text verwenden und gleichzeitig ein Chatsystem haben, in dem eine weltweite Community interagiert, ist es gut möglich, dass das Text-Rendering für Ingame-Text und für das Chatsystem unterschiedlich ist
    • Eine moderne Webbrowser-Engine für GPU-beschleunigtes Text-Rendering neu zu schreiben ist zwar heikel, aber ich dachte, dass das teilweise bereits geschieht
      Laut https://keithclark.co.uk/articles/gpu-text-rendering-in-webk... (2014) „verlieren Elemente, die in aktuellen Versionen von Chrome, Safari oder Opera auf die GPU hochgestuft werden, Subpixel-Antialiasing, und Text wird in Graustufen gerendert“
      Dann frage ich mich, was noch fehlt. Wenn dieser Satz stimmt, müsste doch zumindest ein Teil des Wegs von einem UTF-8-String zum Bitmap auf der GPU möglich sein
  • Beeindruckende Arbeit.
    Subpixel-Antialiasing halte ich persönlich aber für ziemlich sinnlos. In den 2000ern, als man noch 72-dpi-Monitore nutzte, war das ein brauchbarer Hack, aber auf modernen Retina-Displays ist es kaum wahrnehmbar, und man handelt sich für eine sehr kleine Verbesserung mehrere Nachteile ein.
    Es funktioniert nur auf undurchsichtigen Hintergründen, auf das gerasterte Ergebnis lassen sich keine Effekte wie Größenänderung, Spiegelung oder Blur anwenden, und Screenshots sehen auf anderen Displays schlechter aus.

    • Ohne Subpixel-Antialiasing würde vieles deutlich einfacher, aber viele Desktop-Nutzer verwenden immer noch Low-DPI-Monitore.
      Laut Firefox-Hardware-Umfrage [1] nutzen 16 % der Nutzer ein Display mit 1366x768 Auflösung.
      Das ist nicht nur ein Problem alter Hardware: 96-dpi-Monitore und -Laptops werden auch heute noch produziert.
      [1]: https://data.firefox.com/dashboard/hardware
    • Letztlich klingt das nach: „Ich nutze ein High-DPI-Display, also sind mir Leute egal, die das nicht tun.“
      Die anderen Argumente fallen gegenüber den besseren Ergebnissen, die Subpixel-Rendering dort liefert, wo es anwendbar ist, kaum ins Gewicht.
    • Selbst wenn es, wie vom Autor gewünscht, ein Protokoll gäbe, um die Subpixel-Anordnung eines Displays zu ermitteln, und dieses breit übernommen würde, ist es sehr wahrscheinlich, dass einige Hersteller es falsch implementieren und dadurch Rendering-Probleme erzeugen, die für Endnutzer sehr schwer zu verstehen sind.
  • Wichtig ist: SDF berechnet die Pixeldistanz zur nächstgelegenen Kante, während traditionelle Font-Renderer die Pixelabdeckung berechnen.
    Pixelabdeckung ist optimal. Bei kleinen Schriftgrößen kann SDF an Stellen, an denen Kanten zusammentreffen, schlecht aussehen.
    Auf High-PPI-Displays ist das vielleicht weniger problematisch. Ich habe selbst einen SDF-Renderer implementiert, und er sah schlechter aus als FreeType.

    • Die Unterscheidung zwischen Abdeckung und Distanz ist nicht der Kernpunkt. Auch bei Distance-Field-Renderern lässt sich die Abdeckung sehr einfach berechnen.
      Allerdings stimmt der Hinweis, dass ein Distance Field an Schnittpunkten oder allgemein an scharfen Ecken Probleme verursacht.
      Das lässt sich bis zu einem gewissen Grad abmildern, indem man mehrere Distance Fields verwendet und deren Schnittmenge rendert. Ein Beispiel dafür ist https://github.com/Chlumsky/msdfgen.