DirectX: Strukturen und Geschichte

02.11.1999 von Arne Schaepers
Entgegen allen Windows-Prinzipien erlaubt DirectX Manipulationen der Hardware. Doch auch die Art wie die Schnittstelle ins System integriert ist, stellt einen neuen Ansatz dar.

"Die Komponenten von DirectX ermöglichen direkte Zugriffe auf die Hardware des Systems, ohne Programme von der Hardware abhängig zu machen." Die Erklärung, warum das etwas Besonderes sein soll oder ob es sich dabei um einen Widerspruch in sich handelt, führt ein gutes Jahrzehnt zurück - in eine Zeit, in der Büroanwendungen für MS-DOS geschrieben und auf 20 Disketten ausgeliefert wurden: eine für das Programm, der Rest für eine Unzahl von Treibern.

Damit die DOS-Version von MS-Word so etwas Herausragendes wie Fettschrift auf dem Bildschirm zustande brachte, musste Word die Grafikkarte in Eigenregie vom Text- in den Grafikmodus umschalten. Danach machte sich Word direkt am Bildspeicher und den einzelnen Registern des Grafik-Chips zu schaffen. Der einzig angenehme Nebeneffekt: Da das Programm sowieso wissen musste, wie die Bits im Bildspeicher und in irgendwelchen Registern stehen, konnte es sich bei seinen Aktionen auf die Dinge beschränken, die wirklich notwendig waren.

Um beispielsweise einen Buchstaben von Weiß nach Blau umzufärben, wurden ein paar Bits an der richtigen Stelle gekippt - nicht mehr. Mit einer neuen Grafikkarte, für die es jedoch keinen Word-Treiber gab, war das Vergnügen sofort vorbei. Und weil jedes Programm hier sein eigenes Süppchen kochte - Word Perfect und wollte beispielsweise nichts von WordStar wissen und beide hatten mit MS-Word nichts am Hut - war nach dem Kauf einer neuen Grafikkarte unweigerlich die Jagd nach einigen Dutzend programmspezifischer Treiber angesagt.

Generalisierung

Windows hat diese Probleme gründlich beseitigt. Hier liefert der Hersteller der Grafikkarte einen einzigen Treiber, den alle Anwendungen gemeinsam ansprechen. Dies geschieht über einen umfangreichen Satz generischer Funktionen, die das Betriebssystem zur Verfügung stellt.

Der Preis für diese Generalisierung ist jedoch hoch: Eine Windows-Anwendung, die einen Buchstaben von Weiß nach Blau umfärben will, setzt keine Bits im Bildspeicher, sondern gibt dieses Zeichen komplett neu aus. Dabei ruft sie unter anderem den Font-Manager auf, der die Umwandlung der in Bezierkurven definierten Schrift in ein Bitmap besorgt. Auch der Fenster-Manager kommt ins Spiel, damit die Zeichenaktion auf das programmeigene Fenster begrenzt wird. Die Zeichenroutinen der GDI (Graphics Device Interface) haben ein Wörtchen mitzureden, und schließlich der Grafiktreiber für das Einschreiben der Daten in den Bildspeicher. Einige zigtausend Prozessorzyklen später ist schließlich dasselbe Ergebnis erzeugt wie durch den einzigen Befehl eines DOS-Programms.

Ziel: Standardisierte Treiber

Büroanwendern kann man diesen gewaltigen Überbau offensichtlich verkaufen (nach dem Motto: "Holen Sie sich halt ein schnelleres System"). Spieleprogrammierer verweigerten sich dem Trend zu Windows dagegen exakt aus diesem Grund für längere Zeit und schufen so eine Art letzter Bastion von DOS. Dies entwickelte sich zunehmend zum Dorn im Auge von Microsoft, da dieses Betriebssystem nach fast 15 Jahren endlich zu Grabe getragen werden sollte. Einige Ansätze zu standardisierten Treibern gab es durch den Bedarf der Spiele-Entwickler auch unter DOS. Dazu zählen beispielsweise der universelle VESA-Treiber UniVBE von Scitech oder die Sound-Treiber von Miles Design.

Probleme

Auch unter Windows ist ein Satz von Systemfunktionen erforderlich, die einen möglichst direkten Zugriff auf unterschiedliche Hardware ermöglichen sollten. Dieser "hardware-unabhängige Zugriff auf die Hardware" lässt sich weitgehend mit Techniken bewältigen, die schon länger bekannt sind: Wenn ein Programm einen Buchstaben von Weiß nach Blau umfärben will, kann es entweder mit fixen Informationen arbeiten oder diese Daten (Bildspeicher- und Registeradressen, Bits für Farben) abfragen. Dafür musste eine Programmier-Schnittstelle geschaffen werden, die unterschiedliche Hardware-Techniken unter einen Hut bringt. So arbeiten beispielsweise nicht alle Grafikkarten mit einem einzigen Farbregister.

Als großes Problem erwies sich die Integration dieser Schnittstelle in das Betriebssystem Windows. Hier laufen Operationen mit gemeinsamen Ressourcen wie Grafik- und Soundkarte, Maus, Joystick, Tastatur et cetera grundsätzlich nach einem konservativen Schema ab. Das bedingt Vor- und Nachbereitungen, die bei einfachen Aktionen einen wesentlichen Teil des Überbaus, wie in Bild 2 zu sehen, gegenüber DOS ausmachen.

Lösung: Pseudo-Einprozess-System

Leider lässt sich der durch Multitasking und durch die Natur von Windows verursachte Überbau nicht einfach beseitigen. Jedenfalls nicht, wenn dies nicht auf das Neuschreiben des gesamten Betriebssystems hinauslaufen soll. Das war bei der hardwarenahen Programmierung, beispielsweise auf dem Amiga, durchaus üblich. Nicht nur Spiele, auch Anwendungsprogramme basierten auf dem Prinzip "Welche Teile des Betriebssystems schalte ich vor dem Start meines Programms aus?". Die fehlenden Routinen ersetzten die Programmierer durch eigenen Code, der schneller war, mehr Funktionen bot und obendrein kleiner war.

Was sich die Entwickler von Microsoft für DirectX einfallen ließen, war dagegen ein ganzes Bündel von Techniken, von denen wir einige exemplarisch herausgegriffen haben.

Pseudo-Einprozess-System

Pseudo-Einprozess-System: Programme fordern beim System eine Ressource "bis auf weiteres" an. Das System nimmt dabei eine Sicherung des Zustandes vor. Danach bleibt die Ressource so lange im Besitz des Programms, bis ein anderer Prozess seine Wünsche anmeldet. Erst in diesem Moment, also nicht nach jeder Einzelaktion, stellt das System den ursprünglichen Zustand wieder her. Es erlaubt dann dem anderen Prozess den Zugriff - und meldet dem ersten Prozess "Fehler", wenn dieser später versucht, seine Aktionen ohne Reinitialisierung fortzusetzen.

Reaktives Modell mit Rohdaten

Anstatt Fensterkoordinaten und -Grenzen bei jeder Aktion erneut zu berechnen beziehungsweise zu prüfen, werden diese Daten einmal festgelegt und "bis auf weiteres" als statisch betrachtet. Auch hier bekommt das Programm eine Fehlermeldung zurück, wenn sich die Verhältnisse durch einen Task-Wechsel oder ähnliches geändert haben. Es muss sich dann selbst um eine Neuberechnung kümmern.

Rohdaten statt automatischer Umsetzung

Normale Windows-Anwendungen können beispielsweise Bitmaps oder Audiodaten in einem beliebigen Standardformat ausgeben und Windows eventuelle erforderliche Umsetzungen überlassen - wie etwa bei der Darstellung eines TrueColor-Bitmaps auf einem System, dessen Grafikkarte im Hicolor-Mode arbeitet. "Direkt" arbeitende Programme haben ihre Daten dagegen in dem Format zu liefern, auf das die Hardware momentan eingerichtet ist. Das heißt durchaus, dass Programme bei Bitmaps in Eigenregie mit 8, 15, 16, 24 und 32 Bit Farbtiefe zurechtkommen müssen. Von DOS vorbelastete Programmierer sind allerdings bei Spielen wesentlich Schlimmeres als fünf verschiedene Bitmap-Formate gewohnt.

Zusammengefasst: Bei normalen Windows-Anwendungen kümmert sich das System um das gesamte Drumherum und verschwendet dabei unter Umständen eine Unmenge an Rechenzeit. Ein "direkt" arbeitendes Programm muss dagegen die Vor- und Nachbereitungen weitgehend selbst übernehmen. Es wird dabei in den Entscheidungsprozess eingebunden und dadurch auch erheblich "unbequemer" - lies: aufwändiger zu erstellen. Das Ergebnis spricht allerdings für sich: Der für Grafikausgaben um den Faktor 10.000 erhöhte Zeitaufwand ist eher konservativ geschätzt.

Geschichte - Vom Game SDK bis DirectX7

Nachdem Microsoft zunächst nur die Spieleecke im Auge hatte, hörte die erste, 1994 erschienene Version von DirectX auf den Namen "Game SDK". Die Möglichkeiten dieser Programmierschnittstelle waren vergleichsweise bescheiden: Mit ihr konnte man aus Windows heraus auf einen Vollbildmodus umschalten und das eigene Fenster exklusiv mit Beschlag belegen. Das Fenster lässt sich im Vollbild also so lange wie in einem Einprozess-System behandeln, bis ein anderer Prozess dazwischenfunkt.

Hinter diesem Vollbild steckt auch heute noch ein reguläres Fenster, das den gesamten Desktop bedeckt und so dafür sorgt, dass andere Prozesse erst nach einem expliziten Task-Wechsel wieder zum Zuge kommen. Tatsächlich hatte das Game SDK weder eine eigene Architektur noch ein durchgehendes Konzept: Was es bot war zwar revolutionär, weil es dem Grundgedanken von Windows diametral zuwiderlief, aber letztlich wenig mehr als eine Reihe von Einsprüngen in interne Funktionen der Grafiktreiber. Derartiges hatte Microsoft in früheren Zeiten ganz bewusst vor Anwendungsprogrammen versteckt.

Als sich abzeichnete, dass nicht nur Spieleentwickler von diesen Möglichkeiten Gebrauch machen wollten - unter anderem meldete sich bei Microsoft die Excel-Entwicklertruppe zu Wort - bekam das Kind einen neuen Namen, DirectX. Die Architektur sollte nicht mehr nur auf die Manipulation von Fenstern beschränkt sein, sondern auch mit anderen Geräte klarkommen. Zuerst waren es Soundkarten, später dann Joysticks und andere Steuergeräte. Auf der untersten Ebene agieren gerätespezifische Treiber in Kombination mit einer Schicht, die als Hardware Abstraction Layer (HAL) bezeichnet wird. Sie ist für die Ausführung der Aktionen zuständig, die das jeweilige Gerät über direkt eingebaute Funktionen erledigen kann.

Der Emulation Layer emuliert nichts

Die darüberliegende Ebene heißt Hardware Emulation Layer (HEL) und führt Operationen aus, die sich aus mehreren "Hardware-Aktionen" zusammensetzen. Die Windows-GDI garantiert die Ausführung aller offiziell dokumentierten Funktionen und leistet dafür notfalls eine Menge eigener Arbeit und ist nicht zuletzt deshalb mit einem entsprechendem Überbau verbunden. Im Gegensatz dazu kann die HEL bei aufwändigeren Dingen (wie etwa Overlays) ohne weiteres ein schlichtes "die Hardware kann das nicht" zurückmelden - und überlässt es dem Programm, mit dieser Feststellung zurechtzukommen. Das gilt beispielsweise für Nebeleffekte, Anti-Aliasing und so weiter. Um mit einem oft verbreiteten Missverständnis aufzuräumen: Trotz ihres Namens ist die HEL kein "Emulator" für fehlende Funktionen, sondern in erster Linie eine logische Zusammenfassung einfacher, existierender Funktionen.

Mit der Version 3 ging DirectX erstmals über den Direktzugriff auf bereits Bekanntes hinaus: Die neu geschaffene Abteilung Direct3D bietet Funktionen zur dreidimensionalen Darstellung und damit etwas, was in der auf 2D angelegten GDI von Windows überhaupt kein Gegenstück hat. In der Version 5 kam mit Kraftrückmeldungen für Eingabegeräte (z.B. Force Feedback-Joysticks) ein Bereich dazu, der bereits vom thematischen Ansatz her über Windows hinausgeht. Die Version 6 erweiterte die 3D-Funktionalität noch einmal kräftig - und die jüngst erschienene Version 7 versteht sich endgültig nicht mehr als Zusatz, sondern als integraler Bestandteil des Betriebssystems.

Ergebnis: Hardwarebeschleunigung nötig

Trotz aller Optimierungen ist DirectX immer noch wesentlich langsamer als der direkte Zugriff auf Chip-Register und Speicheradresssen unter DOS. Das Durchlaufen zweier Schichten - und seien sie auch noch so dünn - kostet mit Sicherheit fünfzig- oder hundertmal mehr Zeit als eine einfache Schreibaktion. Mit Grafik- und Soundkarten ohne jede Eigenintelligenz wäre DirectX ein schöner Reinfall gewesen, da die Steuerung dort sozusagen in kleinsten Häppchen lief. Wenn der Prozessor für jedes dieser Häppchen fünfzigmal so viel zu tun bekommt, dann läuft das Programm insgesamt auch fünfzigmal langsamer. Mit heutiger Hardware sehen die Verhältnisse anders aus: Was hier ein einzelner Schreibbefehl anstößt, kann ohne weiteres mehreren Milliarden Prozessorbefehlen entsprechen.

Der praktische Beweis für diese Behauptung stammt aus dem Entwicklungskit für DirectX 7: Dort liefert Microsoft einen Referenztreiber mit, der Entwicklern den Umgang mit neuen, noch nicht von der Grafik-Hardware unterstützten Funktionen demonstrieren soll. Sprich, die Funktionen "zu Fuß" über Prozessorbefehle ausrechnet. Obwohl dieser Treiber mit allen erdenklichen Tricks und Optimierungen geschrieben ist, liefert er gerade einmal 0,5 Frames pro Sekunde - und das für die einfachen Demoprogramme des DirectX-SDKs auf einem Pentium III-500. Dass eine richtige Anwendung gut fünfzigmal komplexer als eines dieser Demoprogramme ist und mindestens mit 40 fps laufen sollte, dürfte eher noch konservativ geschätzt sein. Daher muss die Hardware der Grafikkarte dasselbe mit mindestens zweitausendfacher Geschwindigkeit liefern (also mit dem Äquivalent eines Prozessortakts von 1000 Gigahertz). Fünfzig oder hundert zusätzliche Prozessorbefehle zur Vorbereitung einer solchen Aktion kann man gegenüber diesen Horrorzahlen wohl getrost vergessen.

Glide und OpenGL keine Konkurrenz

Wenn es um Laufzeitumgebungen für Spiele geht, wird DirectX gerne mit Glide verglichen. Beim Thema Grafik ist üblicherweise das Stichwort OpenGL fällig. Für sich genommen, haben diese Vergleiche durchaus ihren Sinn - in grosso modo geht es dabei aber eher um Äpfel und Birnen: Glide ist eine Spiele-Schnittstelle, die ausschließlich mit den Grafik-Chips des Herstellers 3Dfx funktioniert, OpenGL eine Hardware- und Betriebssystem-unabhängige Grafik-Schnittstelle, die nichts mit Sound oder Joysticks am Hut hat und bei der Schwerpunkt nicht auf Geschwindigkeit um jeden Preis, sondern auf exakter Darstellung liegt. OpenGL gibt es nicht nur für Windows, sondern auch für Linux und einige andere Unix-Derivate. Wie verschieden die Obstsorten tatsächlich sind, lässt sich nicht zuletzt daran erkennen, dass die OpenGL-Implementation für Grafikkarten von 3Dfx auf Glide aufsetzt.

Nach diesen grundsätzlichen Überlegungen zur Integration von DirectX in Windows und die Funktionsweise der API an sich geht es im zweiten Teil um die einzelnen Module. Inzwischen hat Microsoft alle Ein- und Ausgabefunktionen eines PC mit direkter Unterstützung gesegnet - was aber bisweilen auch zum Fluch werden kann. (nie)