Ponts agnostiques API (c.-à-d. OpenGL / D3D / Whats). Les utilisez-vous, comment vous les faites. Pro's and Con's [fermé]

12

Vous faites un moteur 3D. Vous voulez le meilleur des mondes multiplateformes. Soudain, vous réalisez que si vous souhaitez utiliser Direct3D sur les machines Windows et OpenGL sur OSX / Linux, vous devrez sacrifier les fonctionnalités prises en charge des deux au plus petit dénominateur commun.

Certains peuvent utiliser OpenGL sur trois systèmes d'exploitation, car il semble être le dénominateur le moins commun en soi. Tout est bon. Ensuite, vous devez porter votre backend API graphique sur le GX de Nintendo, vous devez également créer un chemin PS3 et Xbox360.

Que faire? Concevez-vous votre propre API qui est le dénominateur le moins commun en soi et écrivez-vous des implémentations backend pour chaque plate-forme ou écrivez-vous pour chaque plate-forme sa propre branche?

Si vous choisissez de concevoir votre propre API, utilisez-vous un modèle de pont ou votre propre vaudou? Où la folie s'arrête-t-elle là où vous réalisez tout et l'approche d'évier de cuisine doit s'arrêter et vous avez essentiellement un moteur séparé pour chaque plate-forme en tant que branche. Ou vous vous en tenez à tout et à l'évier de cuisine et conservez les spécificités de la plate-forme dans les spécialisations du module backend pour chaque plate-forme.

Image clé
la source

Réponses:

9

Je ne suis pas fan de l'approche du dénominateur le moins commun. Si vous faites cela, vous risquez de vous retrouver avec des fonctionnalités paralysées et de mauvaises performances.

Au lieu de cela, ce que j'ai fait dans le passé, c'est de fournir des fonctionnalités de niveau légèrement supérieur dans une bibliothèque. Cette bibliothèque est (principalement) indépendante de l'API et peut être utilisée n'importe où, mais la mise en œuvre de la bibliothèque est complètement différente pour différentes plates-formes / backends graphiques. Ainsi, par exemple, au lieu d'avoir une fonction SetStateX (), vous avez des fonctions plus élevées comme RenderMesh () ou CreateRenderTarget ().

Ce sera plus de travail qu'une couche vraiment mince chaque fois que vous passerez à une nouvelle plate-forme, mais cela en vaudra la peine car vous serez en mesure d'implémenter les choses de la manière optimale pour cette plate-forme, et vous pourrez prendre avantage de fonctionnalités natives et uniques.

Encore une chose: n'ayez pas peur de casser légèrement l'encapsulation. Il n'y a rien de plus frustrant que de savoir que vous êtes dans une plate-forme avec certaines capacités et que vous ne pouvez pas les utiliser. Laisser une sorte de porte dérobée pour que le code de niveau supérieur puisse profiter de la plate-forme est très utile (par exemple, pouvoir récupérer le périphérique D3D ou le contexte OpenGL).

Noel Llopis
la source
3
Je pense que vous avez dit ce que j'essayais de dire, mais en mieux.
AShelly
6

Tout ce que je peux dire, c'est jeter un œil à Ogre3D . Il est écrit en C ++, Open source (licence MIT maintenant) et fonctionne sur toutes les principales plates-formes. Il résume l'API de rendu et peut passer de l'utilisation de DirectX à OpenGL avec seulement quelques paramètres. Cependant, je ne connais pas suffisamment les différences entre les ensembles de fonctionnalités de DirectX et d'OpenGL pour dire qu'il prend ou ne prend pas en charge une fonctionnalité spécifique.

Torchlight par Runic Games a été écrit en utilisant Ogre et j'ai joué sur Mac et PC et il fonctionne très bien sur les deux.

Casey
la source
2
+1 pour l'approche Ogre. Je le sais, j'ai lu une partie du code. Cependant, j'étais plus intéressé à entendre des histoires personnelles sur l'approche et ce que font les autres dans une situation comme celle-ci.
Image clé
2
Merci! Eh bien, je le ferais principalement comme Ogre. J'ai utilisé l'approche Interface / Factory dans de nombreux développements multiplateformes et je ne sais pas vraiment comment je procéderais autrement. Je dirais cependant que vous devez absolument travailler sur plusieurs plates-formes en même temps. N'essayez pas de tout coder sur Windows et essayez de porter sur Mac par exemple.
Casey
Oui! J'étais de plus en plus fatigué de lancer mon propre wrapper multi-API, puis j'ai commencé à utiliser Ogre. Je n'ai pas regardé en arrière. :)
jacmoe
1

Je n'ai pas fait cela pour les graphiques, mais j'ai créé une boîte à outils audio multiplateforme (PC / XBOX / PS2). Nous avons opté pour la création de notre propre API avec une capacité de dénominateur le moins commun ainsi que des capacités spécifiques à la plate-forme en option. Voici quelques leçons apprises:

La clé est de définir un chemin de traitement qui encapsule les capacités de base de chaque plate-forme et permet la croissance. Pour ce faire, vous devez vraiment comprendre l'API de bas niveau de chaque plate-forme afin de pouvoir identifier les bonnes abstractions. Assurez-vous que la chaîne fonctionne pour la plate-forme la moins performante, tout en donnant accès aux fonctionnalités avancées de la plateforme la plus performante. Faites le travail pour bien faire les choses et vous économiserez beaucoup d'efforts plus tard.

Pour l'audio, la chaîne ressemblait à quelque chose SoundSources -> [Decoders] -> Buffers -> [3D Positioner] ->[Effects] -> Players.

Pour les graphiques, c'est possible Model -> Shapes -> Positioner -> Texturer -> [Lighting] -> [Particle Effects] -> Renderer. (C'est probablement un ensemble complètement faux, je ne suis pas un graphiste).

Écrivez une API front-end qui gère vos objets principaux et un back-end spécifique à la plate-forme qui mappe l'API aux capacités de bas niveau. Fournir un effort optimal pour chaque capacité. Par exemple, sur PC et XBOX, le positionnement audio 3D a été effectué en utilisant les capacités HRTF des puces sonores, tandis que PS2 a utilisé un simple panoramique et fondu. Un moteur graphique peut faire quelque chose de similaire avec l'éclairage.

Implémentez autant de front-end que possible avec un code neutre sur la plateforme. Le code permettant d'attribuer un objet de réverbération à un objet sonore ou un élément de texture à un objet de forme doit être complètement courant, de même que le code pour parcourir et traiter les objets actifs. D'un autre côté, les objets de bas niveau peuvent être entièrement spécifiques à la plate-forme, à l'exception de l'interface commune.

Assurez-vous que l'API ou les fichiers de configuration permettent à l'utilisateur de spécifier des options spécifiques à la plate-forme. Nous avons essayé d'éviter de pousser le code spécifique à la plate-forme au niveau du jeu en le conservant dans les fichiers de configuration: un fichier de configuration d'une plate-forme peut spécifier "effet: SuperDuperParticleGenerator" tandis qu'un autre dit "effet: SoftGlow"

Développer définitivement les plateformes en parallèle. Assurez-vous que les interfaces spécifiques à la plate-forme sont bien définies et testables par elles-mêmes. Cela empêche une grande partie de "est-ce le niveau de la plateforme ou le niveau de l'API?" problèmes lors du débogage.

AShelly
la source
0

J'écris un moteur de jeu open source appelé YoghurtGum pour les plateformes mobiles (Windows Mobile, Android). C'était l'un de mes gros gros problèmes. Je l'ai d'abord résolu comme ceci:

class RenderMethod
{

public:

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

  virtual void* GetSomeData(); 

}

Avez-vous repéré le void*? En effet, RenderMethodDirectDrawrenvoie une surface DirectDraw tandis que RenderMethodDirect3Drenvoie un pool de vertex. Tout le reste était également divisé. J'avais une Spriteclasse qui avait soit un SpriteDirectDrawpointeur soit un SpriteDirect3Dpointeur. Ça craignait.

Ces derniers temps, j'ai beaucoup réécrit les choses. Ce que j'ai maintenant c'est un RenderMethodDirectDraw.dllet un RenderMethodDirect3D.dll. Vous pouvez en fait essayer d'utiliser Direct3D, échouer et utiliser DirectDraw à la place. C'est parce que l'API reste la même.

Si vous voulez créer un sprite, vous ne le faites pas directement mais via une usine. La fabrique appelle ensuite la fonction correcte dans la DLL et la convertit en parent.

Donc, c'est dans l' RenderMethodAPI:

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

Et voici la définition de 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++]);
}

J'espère que cela a du sens. :)

PS J'aurais aimé utiliser STL pour cela, mais il n'y a pas de support sur Android. :(

Fondamentalement:

  • Conservez chaque rendu dans son propre contexte. Soit une DLL, soit une bibliothèque statique, soit juste un tas d'en-têtes. Tant que vous avez un RenderMethodX, SpriteX et StuffX, vous êtes en or.
  • Volez autant que possible à la source Ogre.

EDIT: Oui, il est logique d'avoir des interfaces virtuelles comme celle-ci. Si votre première tentative échoue, vous pouvez essayer une autre méthode de rendu. De cette façon, vous pouvez garder toute votre méthode de rendu de code agnostique.

chevalier666
la source
1
Est-il vraiment judicieux d'avoir une interface virtuelle si vous ne devez jamais avoir plus d'une implémentation active en même temps?
NocturnDragon
0

J'aime utiliser SDL pour cela. Il a des backends de rendu pour D3D, OpenGl, OpenGL ES et une poignée d'autres backends spécifiques à la plate-forme, et il est disponible pour toutes sortes de plates-formes différentes, et actuellement en développement actif, avec des liaisons vers de nombreux langages différents disponibles.

Il résume les différents concepts de rendu et rend la création de vidéo (ainsi que la gestion du son et des entrées et quelques autres choses) disponible dans une API simple et multiplateforme. Et il a été conçu par Sam Lantinga, l'un des principaux développeurs de Blizzard, spécialement pour faciliter le portage de jeux et la création de jeux multiplateformes, de sorte que vous savez que vous avez affaire à une bibliothèque de haute qualité.

Mason Wheeler
la source