¿Por qué no puedo empaquetar un bool y alinearlo en un búfer constante D3D?

9

Muy bien, estoy teniendo dificultades para empacar un bool y alinearlo en un búfer constante hlsl y no estoy seguro de por qué.

Aquí está el buffer en hlsl

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

Y aquí está en c ++

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

He intentado mover el bool y rellenar la estructura de muchas maneras sin suerte. ¿Cuál es la forma correcta de hacer esto?

KlashnikovKid
fuente
¿Cuál es el problema que está causando?
MichaelHouse
El bool se usa para determinar si el sombreador necesita o no una textura. De esta manera puedo renderizar objetos texturizados y no texturizados con el mismo sombreador. El bool simplemente se usa en una declaración condicional. No obtiene los datos correctos porque trata todos los objetos de la misma manera. Esto es incorrecto porque mi esfera del cielo es lo único que tiene una textura en este momento.
KlashnikovKid
Los otros valores funcionan pero no el bool? ¿Has intentado usar uno de los depuradores disponibles para los sombreadores para ver qué se le está agregando?
MichaelHouse
2
intente almacenar el valor bool en un char. almacenar como 1 para verdadero y 0 para falso. Solo para prueba, y también, un bool es 1 byte en C ++ de todos modos ...
Gustavo Maciel
3
El tamaño de un bool depende de la implementación. En algunas plataformas es del mismo tamaño que un int. stackoverflow.com/questions/5067492/…
Tetrad

Respuestas:

9

Para mayor eficiencia, los búferes constantes se asignarán de manera que los valores no se ubiquen en los registros de GPU . Cada registro tiene un tamaño de cuatro flotadores (16 bytes), por lo que las estructuras de búfer constantes deben ser un múltiplo de las mismas en la GPU. Su estructura C ++ debe rellenarse en consecuencia si desea usarla como una conveniencia para mapear datos (esto, nota, no siempre se escala bien).

Su problema, entonces, es que un booleano HLSL tiene cuatro bytes, pero un byte en el lado de la CPU (en su implementación específica). Esto hace que su estructura C ++ no se alinee correctamente: el bit significativo de un valor booleano (el 0 o 1 que importa) se almacenará en el byte menos significativo del valor, y dado que los tamaños no coinciden con la ubicación de ese byte en memoria diferirá en las versiones de CPU y GPU de la estructura.

Insertar manualmente el relleno apropiado y garantizar una alineación adecuada de 16 bytes, o simplemente usar un tipo de tamaño apropiado, como un entero, debería solucionar el problema. Este hilo también puede ser útil para usted, ya que contiene una discusión más profunda sobre aproximadamente el mismo problema.


fuente
1
No entiendo: " isTexturedencaja en el mismo registro, ya que tendría que pasar a horcajadas en el siguiente. Por lo tanto, se incluye por completo en el siguiente registro". El segundo registro consiste specularen los primeros tres componentes y isTextureden el último, por lo que no veo que haya que meter nada en el siguiente registro. La longitud de bool de 1 byte frente a 4 byte obviamente importa, pero cualquiera de los dos encajaría specularen el mismo registro.
Nathan Reed
Estás en lo correcto; Me confundí con el tamaño de los registros versus el tamaño de los tipos y se me ocurrió una representación incorrecta de la asignación. El único problema es la ubicación del byte relevante en la memoria. He ajustado mi respuesta en consecuencia.
Aceptando su respuesta por minuciosidad. Como mencionó, era un problema endian grande / pequeño y el uso de un int lo resolvió.
KlashnikovKid
3

Bien, leí un poco y noté que un hlsl bool es esencialmente un entero de 32 bits. Así que solo usé un int en la estructura c ++ para resolver mi problema.

struct GeometryBufferPass_MaterialBuffer {
    XMFLOAT3 diffuse;
    float specularExponent;
    XMFLOAT3 specular;
    int isTextured;
};
KlashnikovKid
fuente
¿Por qué no mantienes el tipo bool pero solo usas int en el lado del compilador? Solo para mantener la semántica.
Gustavo Maciel
Sí, eso es lo que estoy haciendo arriba. Usó un número entero para el lado de la estructura de la CPU y un bool para el lado constante de la memoria intermedia de la CPU.
KlashnikovKid
0

float, bool e int no se alinean necesariamente para endian, particularmente para artículos múltiples.

Cambiar a int o float funciona en algunos de los ejemplos enumerados aquí, ya que está alineado entre los elementos XMFLOAT3, por lo que se hace referencia correctamente. Sin embargo, si necesita declarar una matriz o varios elementos en la estructura para int, float (ninguno de los tipos XM), probablemente encontrará que los valores de GPU no coinciden con los valores establecidos en la estructura de la CPU.

Ciertamente lo hice al agregar una matriz de tipo int para usar para el tipo de iluminación.

La forma más fácil que encontré es apegarme a los tipos XM que se alinean en 16, lo que puede requerir elementos / bytes desperdiciados, pero clasifica el endian por usted. EG XMINT4 y acaba de usar el primer elemento .x para su valor, o si tiene la necesidad, use los otros elementos para otro propósito pero significa una mala denominación (asegúrese de comentar). Nota: la matriz XMINT2 también estará fuera de orden lógico

DavrosX
fuente