Pour Vertex Buffer Steaming, Multiple glBufferSubData VS Orphaning?

8

J'apprenais OpenGL récemment. Dans les jeux, nous devons mettre à jour la position des objets de jeu fréquemment, et ils entreront et sortiront de l'écran en permanence. Cela signifie donc que dans le rendu, nous devons également mettre à jour le tampon de sommet assez souvent.

Dans le contexte d'OpenGL, une façon intuitive est d'utiliser glBufferSubData pour mettre à jour ceux qui ont changé.

Mais j'ai également lu en ligne une astuce appelée Orphaning qui crée une nouvelle mémoire tampon et y télécharge toutes les données des sommets.

En raison également du coût de l'état et du coût de téléchargement, plusieurs glBufferSubData peuvent également coûter plus cher.

Voici ma question,

  1. Quelle méthode est la meilleure?
  2. Les stands sont-ils vraiment importants dans ce cas?
  3. Les changements d'état et le coût de téléchargement sont-ils vraiment importants dans ce cas?

Merci!

YiFeng
la source
Pourquoi voulez-vous télécharger à nouveau la géométrie alors que vous ne pouvez réellement mettre à jour la matrice? en d'autres termes, vous n'avez pas besoin de télécharger à nouveau les sommets lorsque seules les transformations doivent changer.
concept3d
@ concept3d Ouais, tu as raison! J'ai fait une erreur en écrivant les descriptions des problèmes. Merci de me le rappeler! J'ai déjà mis à jour les descriptions.
YiFeng

Réponses:

9

C'est une question complexe avec beaucoup de petits détails qui comptent vraiment, les performances varient en fonction de la plate-forme et de l'application. Vous devez donc définir les goulots d'étranglement possibles avant d'investir dans des optimisations.

Cela dit, tout d'abord, je suppose que vous devez réduire autant que possible les téléchargements et les mises à jour, par exemple utiliser l'instanciation.

Deuxièmement, notez que les GPU ne peuvent pas transférer les tampons et effectuer le rendu en même temps, donc toutes les commandes OpenGL dans la file d'attente de commandes sont traitées séquentiellement par le périphérique. Il existe différentes manières de copier des données et / ou de les rendre disponibles pour être utilisées par le GPU.

Il existe différentes façons de diffuser des données sur le GPU

1- glBufferDataou glBufferSubDataméthode

Utiliser glBufferDataou glBufferSubDataest comme memcpy. vous passez un pointeur et une opération DMA peut être effectuée, je l'ai dit parce que la mémoire peut être épinglée dans la mémoire du processeur et utilisée directement par le GPU sans qu'un transfert de mémoire vers le GPU ne se produise, en fonction de l'indicateur d'utilisation (GL_STREAM). À votre avis, vous devriez essayer ceci au début, car il est plus simple à mettre en œuvre.

2- obtenir un pointeur sur la mémoire interne en utilisant glMapBuffer

Si ce qui précède n'est pas assez bon, vous pouvez utiliser glMapBufferun pointeur vers la mémoire interne et vous pouvez utiliser ce pointeur pour remplir directement le tampon, c'est bien avec les opérations de lecture et d'écriture de fichier, car vous pouvez directement mapper les données du fichier dans la mémoire du GPU plutôt que de copier d'abord dans un tampon temporaire. Si vous ne voulez pas mapper tout le tampon, vous pouvez utiliser glMapBufferRangece qui peut être utilisé pour mapper une partie du tampon.

Une astuce consiste à créer un grand tampon, à utiliser la première moitié pour le rendu et la seconde moitié pour la mise à jour.

3- Orphelin tampon

En ce qui concerne l'orphelin du tampon, cela peut être fait en utilisant glBufferData avec null et les mêmes paramètres qu'il avait. Le pilote retournera le bloc de mémoire une fois qu'il n'est pas utilisé. Et sera utilisé par le prochain appel glBufferData (aucune nouvelle mémoire ne sera allouée).

Toutes les méthodes mentionnées provoquent beaucoup de synchronisation coûteuse, encore une fois, les GPU ne peuvent pas transférer les tampons et effectuer le rendu en même temps.

4- Unsynchronized Buffers

La méthode la plus rapide (et la plus difficile à obtenir) consiste à utiliser des tampons sans synchronisation GL_MAP_UNSYNCHRONIZED_BITavec glMapBufferRangelesquels vous pouvez utiliser un indicateur , le problème est qu'aucune synchronisation n'est effectuée, nous pouvons donc télécharger des données vers un tampon utilisé et donc tout bousiller. Vous pouvez utiliser plusieurs tampons avec un bit non synchronisé pour rendre les choses un peu plus faciles.

concept3d
la source
0

Je le fais d'une autre manière. Peut-être que quelque chose ne va pas là-bas. Des choses dangereuses cachées.

(J'utilise C # + OpenTK.GameWindow)

Pour l'instanciation de l'objet, j'utilise un objet tampon de tableau séparé pour l'ensemble des matrices de modèle pour chaque instance. Dans le sommet partagé:

#version 400
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
layout (location = 2) in vec2 texcoord;
layout (location = 4) in mat4 model_matrix;

uniform mat4 proj_matrix, view_matrix;

Dans mes matrices de code C # sont stockées dans un tableau flottant float[] mat4_array

Ensuite, je lie le tableau à l'objet tampon de tableau:

public void bind_data_instances()
{
    GL.BindBuffer(BufferTarget.ArrayBuffer, id_InstanceBO);
    GL.BufferData(BufferTarget.ArrayBuffer, mat4_array.Length * sizeof(float), mat4_array, BufferUsageHint.DynamicDraw);
}

À chaque image, les matrices du modèle sont mises à jour. Pour mettre à jour, mat4_arrayj'appelle simplement:

Buffer.BlockCopy(mat4_array_new, 0, mat4_array, 0, sizeof(float) * mat4_models.Length);

et rendre la scène. en utilisant GL.DrawElementsInstanced.

Il n'y a pas d'appels OpenGL supplémentaires et cela fonctionne parfaitement.

Dennis
la source
Quel est le but d'avoir la matrice de modèle comme attribut de sommet?
Anton Duzenko
C'est pour le dessin d'objet d'instance. Et c'est plus rapide qu'un tableau passé comme variable uniforme.
Dennis