0

I have a C++ class Matrix4x4 which has an array of 16 floats and no virtual methods:

class Matrix4x4
{
public:
    float values[16];

    Matrix4x4();
    Matrix4x4(float v0, float v1, float v2, float v3, float v4, float v5, float v6, float v7, float v8, float v9, float v10, float v11, float v12, float v13, float v14, float v15);
    Matrix4x4(const Quaternion &quat, const Vector3 &pos);
    void convertToQuatPos(Quaternion &quat, Vector3 &pos);
    void loadIdentity();
    void setValues(float v0, float v1, float v2, float v3, float v4, float v5, float v6, float v7, float v8, float v9, float v10, float v11, float v12, float v13, float v14, float v15);
    void setPerspective(float fov, float aspect, float nearPlane, float farPlane);
    void setOrthographic(float top, float left, float bottom, float right, float nearPlane, float farPlane);
    ...

    static void multiply3x3(const Matrix4x4 &m1, const Matrix4x4 &m2, Matrix4x4 &result);
    static void multiply4x4(const Matrix4x4 &m1, const Matrix4x4 &m2, Matrix4x4 &result);
    ...

    Matrix4x4 operator*(const Matrix4x4 &a);
    Vector3 operator*(const Vector3 &a);
    Vector4 operator*(const Vector4 &a);
};

I'm passing vectors of these matrices to GLSL like so (where bones is a std::vector of Matrix4x4):

glUniformMatrix4fv(glGetUniformLocation(shader->getProgram(), "boneMatrices"), bones.size(), false, (GLfloat*) &bones[0].values[0]);

which obviously assumes that a Matrix4x4 object will take up only 16-floats worth of memory with no alignment or padding issues. It works fine using MSVC, is it safe to assume that it should work on other platforms (Linux, OSX, Android in the future) e.t.c? Should I be aligning the values array and ensuring there is no padding?

Rajveer
  • 847
  • 11
  • 28
  • This about C not C++ but might be relevant http://stackoverflow.com/questions/1066681/can-c-arrays-contain-padding-in-between-elements – jcoder Nov 06 '13 at 23:08
  • 1
    To start with, if you are worried about portability do not use `float`. OpenGL has its own set of typedefs because the API has stricter rules governing the minimum size in bits and range of floating-/fixed-point data types. Implementations may need to use a different data type than the intrinsic C type to satisfy OpenGL's rules, so `GLfloat` is the data type you should always use to store single-precision floating-point values for use with OpenGL; chances are good it will be the same as your system's `float` type, but there is no portable guarantee. – Andon M. Coleman Nov 06 '13 at 23:57
  • This issue actually does appear on some platforms. iOS, for instance, has a typedef: `CGFloat` that is single-precision (32-bit) on a 32-bit iPhone 5 and double-precision (64-bit) on a 64-bit iPhone 5s. If you pass a function that expects `GLfloat *` a `CGFloat *` you will wish you had not on an iPhone 5s - the compiler is of no help either, it will happily allow you to re-cast the pointer even though the underlying representation is incompatible. – Andon M. Coleman Nov 07 '13 at 00:01
  • @AndonM.Coleman - you haven't really provided a counter-example here; you've introduced a different type. – Brett Hale Nov 07 '13 at 02:57
  • @BrettHale: He was asking about portability between mobile platforms. A lot of iOS examples are written to use `CGFloat`, which up until the introduction of 64-bit iOS on iPhone 5, most developers seemed to assume was always the same size and type as `GLfloat`, even some Apple sample code uses it. C types, and other platform standard types are not the same thing as the GL types, in other words. – Andon M. Coleman Nov 07 '13 at 13:47
  • Hmm that's actually useful advice, I'll keep that in mind when porting to other platforms. Thanks! – Rajveer Nov 07 '13 at 22:12

1 Answers1

2

Adding a static assertion to the header can at least act as a guard against the (very unlikely) possibility that elements of a Matrix4x4 array are padded:

#include <type_traits>
...

static_assert(
    (sizeof(Matrix4x4) == (sizeof(GLfloat) * 16) &&
     std::is_standard_layout<Matrix4x4>::value),

    "Matrix4x4 does not satisfy contiguous storage requirements");

see: std::is_standard_layout

Brett Hale
  • 21,653
  • 2
  • 61
  • 90
  • Excellent, I never knew this existed, looks very useful. Now out of curiosity, if I had a class that had multiple different-sized data members, should I worry about alignment? For example if I were concerned with how an array/vector of a class/struct were written to a binary file, should I also not be concerned with how I'm passing data to something like GLSL (I understand I don't need to in this case, I'm talking about a hypothetical situation)? – Rajveer Nov 07 '13 at 22:26
  • That depends. For vertex attributes you can work without padding, but e.g. for uniform buffers you have to take the layout into account. Also see this excellent answer: http://stackoverflow.com/questions/21676280/glsl-packing-4-float-attributes-into-vec4/21680009#21680009 – Martin Gerhardy Jan 10 '17 at 06:06