Quelle est la bonne approche pour gérer les uniformes dans OpenGL moderne?

8

Je crée un moteur de rendu en utilisant OpenGL moderne (3.1 et plus) et maintenant j'essaye de créer une manière efficace mais flexible de gérer les uniformes. J'ai lu sur des objets tampons uniformes et sur ce qu'est une approche `` commune '' pour les utiliser (ce dernier ne m'a malheureusement pas donné autant de résultats que je l'espérais).

Afin de réduire les appels d'API OpenGL et de stocker des données dans une mémoire contiguë, j'envisage de créer plusieurs grands tampons pour chaque structure de données qui devrait être téléchargée sur le GPU. Chaque tampon a une taille maximale de 16 ko (d'après ce que je comprends, il est garanti que ce sera disponible pour un UBO). Lorsqu'un objet veut pouvoir télécharger des uniformes sur le GPU, il récupère le premier tampon du type à télécharger qui n'est pas encore plein et obtient le prochain index disponible dans ce tampon. Lorsque l'objet est dessiné, il lie l'UBO (s'il n'est pas encore lié) et télécharge l'index d'élément de l'UBO.

Cela se traduit par quelque chose comme ceci:

layout(std140) uniform ModelData { 
    mat4 model_matrix[kNumInstancesPerModelUbo]; 
}
uniform int u_ModelDataIndex;

layout(std140) uniform SkeletonData { 
    mat4 bone_transforms[kNumInstancesPerSkeletonUbo][kMaxBones]; 
}
uniform int u_SkeletonDataIndex;

Cependant, je considère également les éléments suivants:

layout(std140) uniform MeshData {
    mat4 model_matrix[kNumInstancesPerMeshUbo];
    mat4 bone_transforms[kNumInstancesPerMeshUbo][kMaxBones];
}
uniform int u_MeshDataIndex;

À certains égards, cela semble beaucoup plus propre, car il faut un seul index pour accéder à toutes les données liées au maillage à télécharger. D'un autre côté, cela pourrait devenir incontrôlable (taille du tampon supérieure à 16 Ko, gère les données non pertinentes (par exemple, un maillage sans squelette) ou même des problèmes de synchronisation car vous n'êtes pas autorisé à dire les os lors du téléchargement des matrices de modèle) et je ne sais pas non plus comment cela affecterait la disposition de la mémoire sur le GPU.

Franchement, j'ai l'impression d'être coincé ici et je ne peux pas trouver un bon exemple concret de la façon dont vous aborderiez la gestion rapide et flexible des UBO.

Avez-vous des conseils ou des ressources pour moi qui pourraient m'aider ici?

PhilipMR
la source

Réponses:

2

La sous-allocation à partir d'un tampon plus grand est absolument la voie à suivre, avec des mises en garde. Je viens plus d'un côté DirectX / Vulkan, mais cela devrait s'appliquer également à OpenGL (je n'aurai tout simplement pas d'appels d'API directs ici dans cette réponse). Les choses à considérer sont les suivantes:

  • Avez-vous besoin d'indexer dans le tampon plus grand, ou êtes-vous d'accord pour lier la ressource à l'offset à chaque fois?
  • Avez-vous pris soin de toutes / toutes les restrictions d'alignement pour vos uniformes qui sont emballés ensemble (l'alignement de 256 octets est courant)?

Les API graphiques plus récentes ont un "décalage dynamique" que vous pouvez spécifier avec la commande draw qui est un moyen assez rapide d'accéder indirectement à une sous-région d'un tampon. Cependant, en supposant que vous modifiez trois fois les données qui sont mutables, il devrait y avoir peu ou pas de conflit dans le pilote pour lier les données (juste une surcharge fixe).

En résumé, oui, l'allocation de plus grandes régions de mémoire / tampons et la sous-location de ces régions est considérée comme la meilleure pratique. Cela s'applique même aux objets avec des shaders différents (si votre allocateur peut le gérer).

jeremyong
la source
0

Incluez une phase d'analyse comparative des deux solutions dans votre application, puis sélectionnez la solution gagnante lors de l'exécution. C'est simple, portable et à l'épreuve du temps. Je veux dire, vous avez un test pour ça, non? ;-)

Je sais que c'est une réponse assez générique aux "meilleures pratiques" pour des performances élevées, mais si vous y pensez, il y a des milliers de configurations cibles possibles et de nombreux fournisseurs à considérer. Si vous avez besoin de ce petit plus, allez payer votre fournisseur pour un pilote optimisé pour votre application.

Andreas
la source