Dlaczego nie mogę spakować bool i wyrównać do stałego bufora D3D?

9

W porządku, trudno mi się spakować bool i ustawić w buforze ciągłym hlsl i nie jestem pewien dlaczego.

Oto bufor w hlsl

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

I tutaj jest w c ++

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

Próbowałem przesuwać bool i wypełniać strukturę na wiele sposobów bez powodzenia. Jaki jest właściwy sposób to zrobić?

KlashnikovKid
źródło
Jaki to problem?
MichaelHouse
Wartość bool służy do ustalenia, czy moduł cieniujący musi próbkować teksturę. W ten sposób mogę renderować teksturowane i nie teksturowane obiekty za pomocą tego samego modułu cieniującego. Wartość bool jest po prostu używana w instrukcji warunkowej. Nie otrzymuje poprawnych danych, ponieważ traktuje wszystkie obiekty tak samo. Jest to nieprawidłowe, ponieważ moja kula nieba jest jedyną rzeczą, która ma w tej chwili teksturę.
KlashnikovKid
Inne wartości działają, ale nie bool? Czy próbowałeś użyć jednego z dostępnych debugerów dla shaderów, aby zobaczyć, co się w nim dzieje?
MichaelHouse
2
spróbuj zapisać wartość bool w char. zapisz jako 1 dla wartości true i 0 dla wartości false. Tylko na test, bool i tak ma 1 bajt w C ++ ...
Gustavo Maciel
3
Wielkość bool zależy od implementacji. Na niektórych platformach ma ten sam rozmiar co int. stackoverflow.com/questions/5067492/…
Tetrad

Odpowiedzi:

9

W celu zwiększenia wydajności, stałe bufory będą odwzorowane tak, że wartości nie będą przechodzić przez rejestry GPU . Każdy rejestr ma cztery zmiennoprzecinkowe rozmiary (16 bajtów), więc stałe struktury buforów muszą być wielokrotnością ich na GPU. Struktura C ++ powinna zostać odpowiednio uzupełniona, jeśli chcesz użyć jej jako wygody do mapowania danych (ta uwaga, nie zawsze dobrze się skaluje).

Problem polega na tym, że wartość logiczna HLSL ma cztery bajty, ale jeden bajt po stronie procesora (w konkretnej implementacji). Powoduje to, że struktura C ++ nie jest odpowiednio wyrównywana: znaczący bit wartości logicznej (0 lub 1, która ma znaczenie) będzie przechowywany w najmniej znaczącym bajcie wartości, a ponieważ rozmiary nie zgadzają się z lokalizacją tego bajtu w pamięci będzie się różnić w wersjach struktury CPU i GPU.

Ręczne wstawienie odpowiedniego dopełnienia i zapewnienie prawidłowego wyrównania do 16 bajtów lub po prostu użycie odpowiedniego rozmiaru, takiego jak liczba całkowita, powinno rozwiązać problem. Ten wątek może być również przydatny, ponieważ zawiera bardziej dogłębną dyskusję na temat tego samego problemu.


źródło
1
Nie podążam za: „ isTexturedzmieści się w tym samym rejestrze, ponieważ musiałby on przechodzić do następnego. W ten sposób zostaje całkowicie zderzony z następnym rejestrem”. Drugi rejestr składa się z specularpierwszych trzech składników i isTexturedostatniego, więc nie widzę, że coś trzeba wpaść na następny rejestr? Długość boola 1-bajt vs 4-bajt ma oczywiście znaczenie, ale każdy z nich zmieściłby się specularw tym samym rejestrze.
Nathan Reed,
Masz rację; Myliłem się co do wielkości rejestrów w porównaniu z wielkością typów i wpadłem na niepoprawną reprezentację odwzorowania. Jedynym problemem jest lokalizacja odpowiedniego bajtu w pamięci. Odpowiednio dostosowałem swoją odpowiedź.
Przyjmowanie odpowiedzi za dokładność. Jak wspomniałeś, był to duży / mały problem dotyczący endianów i użycie int rozwiązało go.
Klashnikov,
3

W porządku, przeczytałem trochę i zauważyłem, że hlsl bool jest w zasadzie 32-bitową liczbą całkowitą. Więc użyłem int w strukturze c ++, aby rozwiązać mój problem.

struct GeometryBufferPass_MaterialBuffer {
    XMFLOAT3 diffuse;
    float specularExponent;
    XMFLOAT3 specular;
    int isTextured;
};
KlashnikovKid
źródło
Dlaczego nie zachowujesz typu bool, ale po prostu używasz int po stronie kompilatora? Aby zachować semantykę.
Gustavo Maciel
Tak, właśnie to robię powyżej. Użyto liczby całkowitej dla strony struct cpu i bool dla strony stałej bufora gpu.
KlashnikovKid
0

float, bool i int nie wymagają ustawiania w linii dla endianu, szczególnie w przypadku wielu przedmiotów.

Przełączanie na int lub float działa w niektórych przykładach wymienionych tutaj, ponieważ jest wyrównane między elementami XMFLOAT3, więc są poprawnie przywoływane. Jeśli jednak musisz zadeklarować tablicę lub kilka elementów w strukturze dla int, float (brak typów XM), prawdopodobnie przekonasz się, że wartości GPU nie pasują do wartości ustawionych w strukturze CPU.

Z pewnością zrobiłem to, dodając tablicę typu int, która będzie używana jako typ oświetlenia.

Najprostszym sposobem, jaki znalazłem, jest trzymanie się typów XM, które wyrównują się o 16, co może wymagać zmarnowanych elementów / bajtów, ale sortuje dla ciebie endian. EG XMINT4 i właśnie użył pierwszego elementu .x dla swojej wartości lub, jeśli masz taką potrzebę, użyj innych elementów w innym celu, ale oznacza to złe nazewnictwo (pamiętaj o komentarzu). Uwaga: Tablica XMINT2 również nie będzie logiczna

DavrosX
źródło