Pourquoi ne puis-je pas obtenir un bool emballé et aligné dans un tampon constant D3D?

9

D'accord, j'ai du mal à obtenir un bool emballé et aligné dans un tampon constant hlsl et je ne sais pas pourquoi.

Voici le tampon en hlsl

cbuffer MaterialBuffer : register(b1) {
    float3 materialDiffuseAlbedo;
    float  materialSpecularExponent;
    float3 materialSpecularAlbedo;
    bool isTextured;
};

Et le voici en c ++

struct GeometryBufferPass_MaterialBuffer {
    XMFLOAT3 diffuse;
    float specularExponent;
    XMFLOAT3 specular;
    bool isTextured;
};

J'ai essayé de déplacer le booléen et de remplir la structure de toutes sortes de façons sans succès. Quelle est la bonne façon de procéder?

KlashnikovKid
la source
Quel est le problème que cela cause?
MichaelHouse
Le booléen est utilisé pour déterminer si le shader doit ou non échantillonner une texture. De cette façon, je peux rendre des objets texturés et non texturés avec le même shader. Le booléen est simplement utilisé dans une instruction conditionnelle. Il n'obtient pas les données correctes car il traite tous les objets de la même manière. C'est incorrect parce que ma sphère de ciel est la seule chose qui a une texture pour le moment.
KlashnikovKid
Les autres valeurs fonctionnent mais pas le bool? Avez-vous essayé d'utiliser l'un des débogueurs disponibles pour les shaders pour voir ce qui y est mis?
MichaelHouse
2
essayez de stocker la valeur booléenne dans un caractère. stocker comme 1 pour vrai et 0 pour faux. Juste pour le test, et aussi, un bool est de 1 octet en C ++ de toute façon ...
Gustavo Maciel
3
La taille d'un booléen dépend de l'implémentation. Sur certaines plateformes, c'est la même taille qu'un int. stackoverflow.com/questions/5067492/…
Tetrad

Réponses:

9

Pour plus d'efficacité, les tampons constants seront mappés de telle sorte que les valeurs ne chevauchent pas les registres GPU . Chaque registre a une taille de quatre flottants (16 octets), donc les structures de tampon constantes doivent être un multiple de celui-ci sur le GPU. Votre structure C ++ doit être complétée en conséquence si vous souhaitez l'utiliser comme commodité pour mapper des données (cela, notez-le, n'est pas toujours bien mis à l'échelle).

Votre problème est donc qu'un booléen HLSL fait quatre octets, mais un octet côté CPU (dans votre implémentation spécifique). Cela provoque que votre structure C ++ ne s'aligne pas correctement: le bit significatif d'une valeur booléenne (le 0 ou 1 qui importe) va être stocké dans l'octet le moins significatif de la valeur, et puisque les tailles ne correspondent pas à l'emplacement de cet octet en mémoire sera différent dans les versions CPU et GPU de la structure.

L'insertion manuelle du remplissage approprié et la garantie d'un alignement correct sur 16 octets, ou tout simplement en utilisant un type de taille appropriée, comme un entier, devraient résoudre le problème. Ce fil peut également vous être utile car il contient une discussion plus approfondie sur à peu près le même problème.


la source
1
Je ne suis pas: " isTexturedrentre dans ce même registre car il devrait chevaucher dans le suivant. Ainsi, il est entièrement transféré dans le registre suivant." Le deuxième registre se compose des speculartrois premiers composants et isTextureddu dernier, donc je ne vois pas quoi que ce soit doit être heurté dans le prochain registre? La longueur booléenne 1 octet vs 4 octets importe évidemment, mais l'un ou l'autre s'inscrirait ensuite speculardans le même registre.
Nathan Reed
Vous avez raison; Je me suis confondu avec la taille des registres par rapport à la taille des types et j'ai trouvé une représentation incorrecte du mappage. Le seul problème est l'emplacement de l'octet correspondant en mémoire. J'ai ajusté ma réponse en conséquence.
Accepter votre réponse pour la minutie. Comme vous l'avez mentionné, c'était un gros / petit problème endian et l'utilisation d'un int l'a résolu.
KlashnikovKid
3

D'accord, j'ai lu un peu et remarqué qu'un booléen hlsl est essentiellement un entier 32 bits. J'ai donc utilisé un int dans la structure c ++ pour résoudre mon problème.

struct GeometryBufferPass_MaterialBuffer {
    XMFLOAT3 diffuse;
    float specularExponent;
    XMFLOAT3 specular;
    int isTextured;
};
KlashnikovKid
la source
Pourquoi ne gardez-vous pas le type bool mais utilisez simplement int du côté du compilateur? Juste pour garder la sémantique.
Gustavo Maciel
Oui, c'est ce que je fais ci-dessus. Utilisé un entier pour le côté struct cpu et un bool pour le côté gpu du tampon constant.
KlashnikovKid
0

float, bool et int ne sont pas nécessairement alignés pour endian, en particulier pour plusieurs éléments.

Le passage à int ou float fonctionne dans certains des exemples répertoriés ici car il est aligné entre les éléments XMFLOAT3 et sont donc référencés correctement. Cependant, si vous devez déclarer un tableau ou plusieurs éléments dans la structure pour int, float (aucun type XM), vous constaterez probablement que les valeurs GPU ne correspondent pas aux valeurs définies dans la structure CPU.

Je l'ai certainement fait lors de l'ajout d'un tableau de type int à utiliser pour le type d'éclairage.

Le moyen le plus simple que j'ai trouvé est de s'en tenir aux types XM qui s'alignent par 16, ce qui peut nécessiter des éléments / octets gaspillés mais trie l'endian pour vous. EG XMINT4 et juste utilisé le premier élément .x pour votre valeur, ou si vous en avez besoin, utilisez les autres éléments dans un autre but mais signifie une mauvaise dénomination (assurez-vous de commenter). Remarque: le tableau XMINT2 sera également hors de l'ordre logique

DavrosX
la source