API Agnostic Bridges (dh OpenGL / D3D / Whatever). Benutzt du sie, wie du sie herstellst? Vor- und Nachteile [geschlossen]


12

Sie machen eine 3D-Engine. Sie wollen das Beste aus Multiplattform-Welten. Plötzlich wird Ihnen klar, dass Sie, wenn Sie Direct3D auf Windows-Computern und OpenGL unter OSX / Linux verwenden möchten, die unterstützten Funktionen beider auf den kleinsten gemeinsamen Nenner bringen müssen.

Einige verwenden OpenGL möglicherweise für drei Betriebssysteme, da es anscheinend der kleinste gemeinsame Nenner für sich ist. Alles ist gut. Anschließend müssen Sie Ihr Grafik-API-Backend auf Nintendos GX portieren und einen PS3- und Xbox360-Pfad erstellen.

Wie geht's? Entwerfen Sie Ihre eigene API, die an sich den kleinsten gemeinsamen Nenner hat, und schreiben Sie Back-End-Implementierungen für jede Plattform, oder schreiben Sie für jede Plattform einen eigenen Zweig?

Wenn Sie sich für das Entwerfen Ihrer eigenen API entscheiden, verwenden Sie ein Brückenmuster oder Ihren eigenen Voodoo? Wo hört der Wahnsinn auf, wo man merkt, dass alles aufhört und die Küchenspülmethode aufhören muss und man im Grunde für jede Plattform einen eigenen Motor als Zweig hat. Oder Sie bleiben bei allem und der Küchenspüle und behalten die Plattformspezifikationen in den Back-End-Modulspezialisierungen für jede Plattform bei.

Antworten:


9

Ich bin kein Fan des Ansatzes des kleinsten gemeinsamen Nenners. Wenn Sie dies tun, kann dies zu einer Beeinträchtigung der Funktionen und einer schlechten Leistung führen.

Stattdessen habe ich in der Vergangenheit etwas mehr Funktionen in einer Bibliothek bereitgestellt. Diese Bibliothek ist (meistens) API-unabhängig und kann überall verwendet werden, aber die Implementierung der Bibliothek ist für verschiedene Plattformen / Grafik-Backends völlig unterschiedlich. Anstatt eine SetStateX () - Funktion zu haben, haben Sie beispielsweise höhere Funktionen wie RenderMesh () oder CreateRenderTarget ().

Es wird mehr Arbeit als eine wirklich dünne Schicht sein, wenn Sie auf eine neue Plattform umsteigen, aber es wird sich voll und ganz lohnen, weil Sie in der Lage sind, die Dinge auf die optimale Weise für diese Plattform umzusetzen, und Sie werden in der Lage sein zu nehmen Vorteil der nativen, einzigartigen Funktionen.

Eine weitere Sache: Haben Sie keine Angst, die Verkapselung leicht zu beschädigen. Nichts ist frustrierender, als zu wissen, dass Sie sich auf einer Plattform mit bestimmten Funktionen befinden und diese nicht nutzen können. Es ist sehr nützlich, eine Hintertür zu hinterlassen, damit Code auf höherer Ebene die Plattform nutzen kann (z. B. das Abrufen des D3D-Geräts oder des OpenGL-Kontexts).


3
Ich denke, du hast gesagt, was ich sagen wollte, nur besser.
AShelly

6

Ich kann nur einen Blick auf Ogre3D werfen . Es ist in C ++, Open Source (jetzt MIT-Lizenz) geschrieben und läuft auf jeder wichtigen Plattform ab Werk. Es abstrahiert die Rendering-API und kann mit nur wenigen Einstellungen von DirectX zu OpenGL wechseln. Ich weiß jedoch nicht genug über die Unterschiede zwischen den Funktionssätzen von DirectX und OpenGL, um zu sagen, dass es eine bestimmte Funktion unterstützt oder nicht.

Torchlight von Runic Games wurde mit Ogre geschrieben und ich habe es auf Mac und PC gespielt und es läuft auf beiden sehr gut.


2
+1 für Oger-Annäherung. Ich weiß davon, habe einen Teil des Codes durchgelesen. Ich war mehr daran interessiert, persönliche Geschichten über den Ansatz zu hören und was andere Leute in einer solchen Situation tun.
Keyframe 14.07.10 um 23:53

2
Vielen Dank! Nun, ich würde es meistens so machen wie Ogre. Ich habe den Interface / Factory-Ansatz in vielen plattformübergreifenden Entwicklungen verwendet und bin mir eigentlich nicht sicher, wie ich es sonst machen würde. Ich würde jedoch sagen, dass Sie unbedingt auf mehreren Plattformen gleichzeitig arbeiten müssen. Versuchen Sie nicht, alles unter Windows zu programmieren, und versuchen Sie dann beispielsweise, auf einen Mac zu portieren.
Casey

Ja! Ich war es wirklich leid, meinen eigenen Cross-API-Wrapper zu rollen, und habe gerade angefangen, Ogre zu verwenden. Hab nicht zurückgeschaut. :)
jacmoe

1

Für Grafiken habe ich das nicht gemacht, aber ich habe ein plattformübergreifendes Audio-Toolkit (PC / XBOX / PS2) erstellt. Wir sind den Weg gegangen, eine eigene API mit Funktionen für den kleinsten gemeinsamen Nenner sowie optionalen plattformspezifischen Funktionen zu erstellen. Hier sind einige Lektionen gelernt:

Der Schlüssel besteht darin, einen Verarbeitungspfad zu definieren, der die Kernfunktionen jeder Plattform zusammenfasst und Wachstum ermöglicht. Dazu müssen Sie die Low-Level-API jeder Plattform wirklich verstehen, damit Sie die richtigen Abstraktionen identifizieren können. Stellen Sie sicher, dass die Kette für die am wenigsten leistungsfähige Plattform funktioniert, und bieten Sie gleichzeitig Zugriff auf die erweiterten Funktionen der leistungsfähigsten patform. Tun Sie die Arbeit, um dies richtig zu machen, und Sie werden später eine Menge Aufwand sparen.

Für Audio war die Kette so etwas wie SoundSources -> [Decoders] -> Buffers -> [3D Positioner] ->[Effects] -> Players.

Bei Grafiken kann es sein Model -> Shapes -> Positioner -> Texturer -> [Lighting] -> [Particle Effects] -> Renderer. (Dies ist wahrscheinlich ein völlig falscher Satz, ich bin kein Grafiker).

Schreiben Sie eine Front-End-API, die Ihre Kernobjekte verarbeitet, und ein plattformspezifisches Back-End, das die API den Funktionen auf niedriger Ebene zuordnet. Geben Sie für jede Funktion die bestmögliche Leistung an. Beispielsweise wurde auf dem PC und der XBOX die 3D-Audiopositionierung mithilfe der HRTF-Funktionen der Soundchips durchgeführt, während für PS2 ein einfaches Schwenken und Überblenden verwendet wurde. Eine Grafik-Engine könnte etwas Ähnliches mit der Beleuchtung tun.

Implementieren Sie so viel Front-End wie möglich mit plattformneutralem Code. Der Code zum Anpassen eines Reverb-Objekts an ein Sound-Objekt oder eines Textur-Assets an ein Shape-Objekt sollte ebenso wie der Code zum Durchlaufen und Verarbeiten aktiver Objekte vollständig identisch sein. Andererseits können die Low-Level-Objekte bis auf die gemeinsame Schnittstelle vollständig plattformspezifisch sein.

Stellen Sie sicher, dass die API oder die Konfigurationsdateien es dem Benutzer ermöglichen, plattformspezifische Optionen anzugeben. Wir haben versucht, zu vermeiden, dass plattformspezifischer Code auf die Spielebene verschoben wird, indem er in Konfigurationsdateien gespeichert wird: In der Konfigurationsdatei einer Plattform kann "effect: SuperDuperParticleGenerator" angegeben werden, während in einer anderen "effect: SoftGlow" angegeben wird.

Definitiv die Plattformen parallel entwickeln. Stellen Sie sicher, dass die plattformspezifischen Schnittstellen gut definiert und eigenständig testbar sind. Dies verhindert eine Menge der "Ist es die Plattform-Ebene oder die API-Ebene?" Probleme beim Debuggen.


0

Ich schreibe eine Open-Source-Spiele-Engine namens YoghurtGum für mobile Plattformen (Windows Mobile, Android). Dies war eines meiner großen Probleme. Zuerst habe ich es so gelöst:

class RenderMethod
{

public:

  virtual bool Init();
  virtual bool Tick();
  virtual bool Render();

  virtual void* GetSomeData(); 

}

Hast du das gesehen void*? Dies liegt daran, dass RenderMethodDirectDraweine DirectDraw-Oberfläche und RenderMethodDirect3Dein Vertex-Pool zurückgegeben werden. Alles andere war ebenfalls gespalten. Ich hatte eine SpriteKlasse, die entweder einen SpriteDirectDrawZeiger oder einen SpriteDirect3DZeiger hatte. Es hat irgendwie gelutscht.

In letzter Zeit habe ich viele Dinge umgeschrieben. Was ich jetzt habe, ist ein RenderMethodDirectDraw.dllund ein RenderMethodDirect3D.dll. Sie können sogar versuchen, Direct3D zu verwenden, fehlschlagen und stattdessen DirectDraw verwenden. Das liegt daran, dass die API gleich bleibt.

Wenn Sie ein Sprite erstellen möchten, tun Sie dies nicht direkt, sondern über eine Factory. Die Factory ruft dann die richtige Funktion in der DLL auf und konvertiert sie in eine übergeordnete Funktion.

Das ist also in der RenderMethodAPI:

virtual Sprite* CreateSprite(const char* a_Name) = 0;

Und das ist die Definition in RenderMethodDirectDraw:

Sprite* RenderMethodDirectDraw::CreateSprite(const char* a_Name)
{
    bool found = false;
    uint32 i;
    for (i = 0; i < m_SpriteDataFilled; i++)
    {
        if (!strcmp(m_SpriteData[i].name, a_Name))
        {
            found = true;
            break;
        }
    }

    if (!found) 
    {
        ERROR_EXPLAIN("Could not find sprite named '%s'", a_Name);
        return NULL; 
    }

    if (m_SpriteList[m_SpriteTotal]) { delete m_SpriteList[m_SpriteTotal]; }
    m_SpriteList[m_SpriteTotal] = new SpriteDirectDraw();

    ((SpriteDirectDraw*)m_SpriteList[m_SpriteTotal])->SetData(&m_SpriteData[i]);

    return (m_SpriteList[m_SpriteTotal++]);
}

Ich hoffe das macht Sinn. :)

PS Ich hätte gerne STL verwendet, aber es gibt keine Unterstützung für Android. :(

Grundsätzlich:

  • Behalten Sie jedes Rendering in einem eigenen Kontext. Entweder eine DLL oder eine statische Bibliothek oder nur eine Reihe von Headern. Solange du ein RenderMethodX, SpriteX und StuffX hast, bist du golden.
  • Stehlen Sie so viel wie möglich von der Ogerquelle.

EDIT: Ja, es macht Sinn, virtuelle Schnittstellen wie diese zu haben. Wenn Ihr erster Versuch fehlschlägt, können Sie eine andere Rendermethode ausprobieren. Auf diese Weise können Sie alle Code-Rendering-Methoden unabhängig machen.


1
Ist eine virtuelle Schnittstelle wirklich sinnvoll, wenn nie mehr als eine Implementierung gleichzeitig aktiv sein soll?
NocturnDragon

0

Ich verwende dafür gerne SDL . Es verfügt über Renderer-Backends für D3D, OpenGl, OpenGL ES und eine Handvoll anderer plattformspezifischer Backends. Es ist für alle möglichen Plattformen verfügbar und befindet sich derzeit in der aktiven Entwicklung. Bindungen für viele verschiedene Sprachen sind verfügbar.

Es abstrahiert die verschiedenen Renderer-Konzepte und stellt die Erstellung von Videos (sowie die Verarbeitung von Sound und Eingaben und ein paar anderen Dingen) in einer einfachen plattformübergreifenden API zur Verfügung. Und es wurde von Sam Lantinga, einem der führenden Entwickler von Blizzard, entwickelt, um das Portieren von Spielen und das Erstellen von plattformübergreifenden Spielen zu vereinfachen. So wissen Sie, dass Sie es mit einer hochwertigen Bibliothek zu tun haben.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.