GLM, OpenGL, VBO, Struct Padding
-
Vielen dank für die Klarstellung!
-
Nachtrag: Mit C++11:
static_assert(sizeof(vector3) == sizeof(float[3]), "Incompatible struct padding.");
Dann gibt's zumindest einen Compilerfehler.
-
cooky451 schrieb:
Der Code ist nicht standardkonform und der Autor verlässt sich einfach darauf, dass das struct nicht gepadded wird. (Ist zugegebendermaßen auch relativ unwahrscheinlich.)
wieso nicht standardkonform?
der code ist richtig, sizeof(...) liefert die groesse der struct zurueck, mit padding.(&points[1])-(&points[0])==sizeof(glm::vec3)
egal ob sizeof 12 oder 16 oder ... liefert, es wird uebereinstimmen mit dem was man bekommt wenn man points mittels index addressiert und genau das will man opengl auch uebergeben als pitch.
intern allokiert std::vector auch nur mit new ElementType[size], was zu malloc(size*sizeof(ElmementType)) compatibel ist.cooky451 schrieb:
Nachtrag: Mit C++11:
static_assert(sizeof(vector3) == sizeof(float[3]), "Incompatible struct padding.");
Dann gibt's zumindest einen Compilerfehler.
ja, compilerfehler schon, aber was hat das mit dem code oben zu tun? er verwendet sizeof(glm::vec3) statt 3*sizeof(float) eben damit es sich nicht auf falsche annahmen stuetzt.
-
rapso schrieb:
egal ob sizeof 12 oder 16 oder ... liefert, es wird uebereinstimmen mit dem was man bekommt wenn man points mittels index addressiert und genau das will man opengl auch uebergeben als pitch.
Also ich bin mir relativ sicher, dass OpenGL alle Werte im Speicher direkt hintereinander haben will. Und genau das ist so eben nicht garantiert.
-
Bei glVertexAttribPointer() übergibst du unter anderem den Stride als Parameter. Wenn du diesen auf 0 setzt, dann werden alle Elemente direkt hintereinander erwartet. Das ist in der Regel aber nicht erstrebenswert. Wenn man keinen besonderen Grund hat es anders zu machen, sollte man seine Vertexdaten interleaved halten, also überhaupt in einem struct, das alle Vertexattribute enthält, anstatt separate Arrays für jedes Attribut.
-
dot schrieb:
Bei glVertexAttribPointer() übergibst du unter anderem den Stride als Parameter. Wenn du diesen auf 0 setzt, dann werden alle Elemente direkt hintereinander erwartet.
Damit kannst du höchstens noch steuern wie viel Platz zwischen den Punkten ist, die floats müssen aber eben trotzdem alle hintereinander liegen.
-
Was sie auch tun, glm ist natürlich extra entsprechend gebaut...
-
dot schrieb:
Was sie auch tun
Habe nie etwas anderes behauptet.
Edit: Okay, mit stride könnte man das tatsächlich standardkonform hinbekommen. Darf man dann nur nicht vergessen.
-
dot schrieb:
Was sie auch tun, glm ist natürlich extra entsprechend gebaut...
Ich dachte das sei unvorhersehbar ? Inwiefern kann GLM das dann überhaupt beeinflussen ?
Und OpenGL WILL die Werte hintereinander haben. Der Stride Parameter würde voraussetzen, dass hinter jedem Wert ein bestimmtes Padding-Array/Byte ist, aber wie schon gesagt, das ist leider unvorhersehbar.
Insofern stimmte ich cooky451 voll zu (das war ja meine anfängliche Befürchtung), dass man hier nur mit Arrays am besten arbeiten sollte.
Und warum Interleaved ? Warum nicht mehrere VBOs + VAO ? Hat Interleaved einen Performancegewinn ? Wenn man das getrennt hält, müsste es ja einfacher sein, die Werte unabhängig von einander zu aktualisieren.
-
Foxx90 schrieb:
Inwiefern kann GLM das dann überhaupt beeinflussen ?
Indem es eben das compilerspezifische Objektlayout berücksichtigt. Auch wenn der Standard im Allgemeinen vielleicht kein bestimmtes Layout garantiert, für die jeweiligen Compiler ist sehr gut dokumentiert, wie genau das Layout aussieht. Und im konkreten Fall gibt es da nicht wirklich viel zu berücksichtigen...
Foxx90 schrieb:
Und OpenGL WILL die Werte hintereinander haben. Der Stride Parameter würde voraussetzen, dass hinter jedem Wert ein bestimmtes Padding-Array/Byte ist, aber wie schon gesagt, das ist leider unvorhersehbar.
Nicht unbedingt, über Stride und Offset kannst du die Daten interleaven
Foxx90 schrieb:
Insofern stimmte ich cooky451 voll zu (das war ja meine anfängliche Befürchtung), dass man hier nur mit Arrays am besten arbeiten sollte.
Nun, mit Arrays hätte man sogar ein vom Standard garantiertes Layout. Aber wie gesagt, nur weil der Standard kein Layout garantiert, heißt das noch lange nicht, dass man nicht weiß, was der Compiler macht...
Foxx90 schrieb:
Und warum Interleaved ? Warum nicht mehrere VBOs + VAO ? Hat Interleaved einen Performancegewinn ? Wenn man das getrennt hält, müsste es ja einfacher sein, die Werte unabhängig von einander zu aktualisieren.
Ja, Interleaved hat einen potentiellen Performancevoteil. Aber wenn du die Daten ständig updaten musst, dann werden getrennte VBOs bzw. separate Arrays natürlich wieder potentiell von Vorteil sein...
EDIT: "Die Reihenfolge der Variablen ändern" darf der Compiler übrigens schon laut Standard nicht, zumindest wenn alle Variablen unter den gleichen access specifier fallen. Und im Falle von aufeinanderfolgenden floats wird kein mir bekannter x86 Compiler zwischen die Member Padding einbauen...
-
Da muss man nichts Compilerspezifisches beachten, wenn man es richtig macht.
class vector3 { float data_[3]; };
Ist standard layout, daher &vector3 == &data_[0], das Array liegt garantiert direkt hintereinander im Speicher, und mit stride bekommt man das Padding am Ende des structs weg.
-
Richtig, die glm Vektoren sehen aber nicht so aus, sondern eher so in der Richtung:
struct vec3 { union { struct { float x; float y; float z; }; struct { float r; float g; float b; }; ... }; };
Und da ist laut Standard (wenn man zunächst gleich mal ignoriert, dass der Standard anomye structs nicht kennt) afaik nur garantiert, dass x und r die gleiche Adresse haben und dass ein Pointer auf das struct auf x bzw. r zeigt...
-
Ich glaube, dass hier ist die entsprechende Stelle für vec4
# if(GLM_COMPONENT == GLM_COMPONENT_CXX11) union { # if(defined(GLM_SWIZZLE)) _GLM_SWIZZLE4_2_MEMBERS(value_type, glm::detail::tvec2<value_type>, x, y, z, w) _GLM_SWIZZLE4_2_MEMBERS(value_type, glm::detail::tvec2<value_type>, r, g, b, a) _GLM_SWIZZLE4_2_MEMBERS(value_type, glm::detail::tvec2<value_type>, s, t, p, q) _GLM_SWIZZLE4_3_MEMBERS(value_type, glm::detail::tvec3<value_type>, x, y, z, w) _GLM_SWIZZLE4_3_MEMBERS(value_type, glm::detail::tvec3<value_type>, r, g, b, a) _GLM_SWIZZLE4_3_MEMBERS(value_type, glm::detail::tvec3<value_type>, s, t, p, q) _GLM_SWIZZLE4_4_MEMBERS(value_type, glm::detail::tvec4<value_type>, x, y, z, w) _GLM_SWIZZLE4_4_MEMBERS(value_type, glm::detail::tvec4<value_type>, r, g, b, a) _GLM_SWIZZLE4_4_MEMBERS(value_type, glm::detail::tvec4<value_type>, s, t, p, q) # endif//(defined(GLM_SWIZZLE)) struct{value_type r, g, b, a;}; struct{value_type s, t, p, q;}; struct{value_type x, y, z, w;}; }; # elif(GLM_COMPONENT == GLM_COMPONENT_CXX98) union {value_type x, r, s;}; union {value_type y, g, t;}; union {value_type z, b, p;}; union {value_type w, a, q;}; # if(defined(GLM_SWIZZLE)) // Defines all he swizzle operator as functions GLM_SWIZZLE_GEN_REF_FROM_VEC4(T, detail::tvec4, detail::tref2, detail::tref3, detail::tref4) GLM_SWIZZLE_GEN_VEC_FROM_VEC4(T, detail::tvec4, detail::tvec2, detail::tvec3, detail::tvec4) # endif//(defined(GLM_SWIZZLE)) # else //(GLM_COMPONENT == GLM_COMPONENT_ONLY_XYZW) value_type x, y, z, w; # if(defined(GLM_SWIZZLE)) // Defines all he swizzle operator as functions GLM_SWIZZLE_GEN_REF_FROM_VEC4_COMP(T, detail::tvec4, detail::tref2, detail::tref3, detail::tref4, x, y, z, w) GLM_SWIZZLE_GEN_VEC_FROM_VEC4_COMP(T, detail::tvec4, detail::tvec2, detail::tvec3, detail::tvec4, x, y, z, w) # endif//(defined(GLM_SWIZZLE)) # endif//GLM_COMPONENT
-
Kann es sein, dass wenn man eine Deklaration so macht:
float x, y, z, w statt float x; float y; ... man vlt. dem Compiler "verbieten" Padding dazwischen zuschieben ?
Ich rate nur, und hoffe innerlich
-
Nein
-
Ja, das ist natürlich doof, warum machen die das so? Ist doch auch totaler Quatsch, meine Vektor Klasse sah irgendwie so aus
template <typename T, std::size_t N> class vector { std::array<T, N> data_; };
Genau genommen war's zwar nur eine Spezialisierung für eine Matrix, aber der Punkt ist, dass man so nicht alles doppelt schreiben muss. Kann ich nicht ganz nachvollziehen was glm da macht.
-
Weil sie die gleiche Schnittstelle wie GLSL bieten wollen. Und in GLSL kann ich nunmal z.B. mit .x, .y, .z auf die Elemente der Vektoren zugreifen. Wenn du einen Weg kennst, um das standardkonform umzusetzen, dann bin ich ganz Ohr, ich such seit Jahren nach so einem Weg...
-
Na ja, ich schreibe halt .x(), .y(), .z(). Dass das wirklich ganz genau so wie GLSL aussieht war mir allerdins nicht sonderlich wichtig. (Ist irgendwie auch kaum sinnvoll? Jeder weis was mit .x() gemeint ist.)
Interessant ist nur noch ob man T x() und void x(T) hat, oder T& x() und const T& x().
-
Jo, damit hast du halt mehr Noise in der Syntax. Ich bevorzuge die Variante mit einzelnen floats. Schön wärs natürlich wenn der Standard ein Layout garantieren würde, aber wenn mir alle nötigen Compiler ein entsprechendes Layout garantieren, dann is das fast genau so gut...
-
dot schrieb:
Jo, damit hast du halt mehr Noise in der Syntax.
Also ob ich x oder x() schreibe, ist mir doch recht egal. Jedenfalls spare ich die ganzen Algorithmen 3 mal zu schreiben. Weiter hat man auch noch einen operator [], und so etwas wie xy(), xz(), ... wie in GLSL ist auch möglich. Ich sehe wirklich keinen Vorteil in einzelnen floats. Aber jedem das Seine.