<?xml version="1.0" encoding="UTF-8" standalone="yes"?><oembed><version><![CDATA[1.0]]></version><provider_name><![CDATA[The ryg blog]]></provider_name><provider_url><![CDATA[https://fgiesen.wordpress.com]]></provider_url><author_name><![CDATA[fgiesen]]></author_name><author_url><![CDATA[https://fgiesen.wordpress.com/author/fgiesen/]]></author_url><title><![CDATA[Row-major vs. column-major and GL&nbsp;ES]]></title><type><![CDATA[link]]></type><html><![CDATA[<p>There&#8217;s two major (no pun intended) ways to store 2D arrays: Row-major and Column-major. Row-major is the default layout in C, Pascal and most other programming languages; column-major is the default in FORTRAN and some numeric math-centric languages (mainly Matlab and R) &#8211; presumably because they started out as a kind of frontend for FORTRAN code.</p>
<p>Confusingly, the same terminology is also used by some people to denote whether you&#8217;re treating vectors as column vectors or row vectors by default. If you treat them as column vectors, you typically multiply a vector with a matrix from the left, i.e. the result of transforming a vector v by a matrix M is written <img src="https://s0.wp.com/latex.php?latex=Mv&#038;bg=ffffff&#038;fg=000&#038;s=0&#038;c=20201002" srcset="https://s0.wp.com/latex.php?latex=Mv&#038;bg=ffffff&#038;fg=000&#038;s=0&#038;c=20201002 1x, https://s0.wp.com/latex.php?latex=Mv&#038;bg=ffffff&#038;fg=000&#038;s=0&#038;c=20201002&#038;zoom=4.5 4x" alt="Mv" class="latex" />. Transforming a vector by M then N is written as <img src="https://s0.wp.com/latex.php?latex=NMv&#038;bg=ffffff&#038;fg=000&#038;s=0&#038;c=20201002" srcset="https://s0.wp.com/latex.php?latex=NMv&#038;bg=ffffff&#038;fg=000&#038;s=0&#038;c=20201002 1x, https://s0.wp.com/latex.php?latex=NMv&#038;bg=ffffff&#038;fg=000&#038;s=0&#038;c=20201002&#038;zoom=4.5 4x" alt="NMv" class="latex" />, which I thought was backwards and confusing when I first saw it, but it has the big advantage of being consistent with the way we usually write function evaluation and composition: <img src="https://s0.wp.com/latex.php?latex=NMv+%3D+N%28M%28v%29%29+%3D+%28N+%5Ccirc+M%29%28v%29&#038;bg=ffffff&#038;fg=000&#038;s=0&#038;c=20201002" srcset="https://s0.wp.com/latex.php?latex=NMv+%3D+N%28M%28v%29%29+%3D+%28N+%5Ccirc+M%29%28v%29&#038;bg=ffffff&#038;fg=000&#038;s=0&#038;c=20201002 1x, https://s0.wp.com/latex.php?latex=NMv+%3D+N%28M%28v%29%29+%3D+%28N+%5Ccirc+M%29%28v%29&#038;bg=ffffff&#038;fg=000&#038;s=0&#038;c=20201002&#038;zoom=4.5 4x" alt="NMv = N(M(v)) = (N &#92;circ M)(v)" class="latex" /> (treating a matrix and its associated linear map given the standard basis as the same thing here). This is why most Maths and Physics texts generally treat vectors as column vectors (unless specified otherwise). The &#8220;row-major&#8221; convention defaults to row vectors, which means you end up with reverse order: <img src="https://s0.wp.com/latex.php?latex=vMN&#038;bg=ffffff&#038;fg=000&#038;s=0&#038;c=20201002" srcset="https://s0.wp.com/latex.php?latex=vMN&#038;bg=ffffff&#038;fg=000&#038;s=0&#038;c=20201002 1x, https://s0.wp.com/latex.php?latex=vMN&#038;bg=ffffff&#038;fg=000&#038;s=0&#038;c=20201002&#038;zoom=4.5 4x" alt="vMN" class="latex" />. This matches &#8220;reading order&#8221; (take v, transform by M, transform by N) but now you need to reverse the order when you look at the associated linear maps; this is generally more trouble than it&#8217;s worth.</p>
<p>Historically, IRIS GL used the row-vector convention, then OpenGL (which was based on IRIS GL) switched to column vectors in its specification (to make it match up better with standard mathematical practice) but at the same time switched storage layout from row-major to column-major to make sure that existing IRIS GL code didn&#8217;t break. That&#8217;s a somewhat unfortunate legacy, since C defaults to row-major storage, so you would normally expect a C library to use that too. ES got rid of a lot of other historical ballast, so this would&#8217;ve been a good place to change it.</p>
<p>Anyway, a priori, there&#8217;s no huge reason to strongly prefer one storage layout over the other. However in some cases, external constraints tilt the balance. I recently bitched a bit about OpenGL ES favoring column-major order, because it happens to be such a case, and column-major is the wrong choice. Don&#8217;t get me wrong, it&#8217;s by no means a big deal anyway, but it makes things less orthogonal than they need to be, which annoys me.</p>
<p>GLSL and HLSL have vec4/float4 as their basic native vector data type, and shader constants are usually passed in groups of 4 floats (as of D3D10+ HW this is a bit more freeform, but the alignment/packing rules are still float4-centric). In a row-major layout, a 4&#215;4 matrix gets stored as</p>
<pre>
struct mat4_row_major {
  vec4 row0;
  vec4 row1;
  vec4 row2;
  vec4 row3;
};
</pre>
<p>and multiplying a matrix with a 4-vector gets computed as</p>
<pre>
  // This implements o = M*v
  o.x = dot(M.row0, v);
  o.y = dot(M.row1, v);
  o.z = dot(M.row2, v);
  o.w = dot(M.row3, v);
</pre>
<p>whereas for column-major storage layout you get</p>
<pre>
struct mat4_col_major {
  vec4 col0;
  vec4 col1;
  vec4 col2;
  vec4 col3;
};

  // M*v expands to...
  o = M.col0 * v.x;
  o += M.col1 * v.y;
  o += M.col2 * v.z;
  o += M.col3 * v.w;
</pre>
<p>so column-major uses muls/multiply-adds whereas row-major storage ends up using dot products. Same difference, so far &#8211; generally, shaders take the exact same time for both variants. But there&#8217;s an important special case: affine transforms, i.e. ones for which the last row of the matrix is &#8220;0 0 0 1&#8221;. Generally almost all of the transforms you&#8217;ll use in a game/rendering engine, except for the final projection transform, are of this form. More concretely, all of the transforms you&#8217;ll normally use for character skinning are affine, and if you do skinning in a shader you&#8217;ll use a lot of them, so their size matters. With the row-major layout you can just drop the last row and do this:</p>
<pre>
  // M*v, where M is affine with last row not stored
  o.x = dot(M.row0, v);
  o.y = dot(M.row1, v);
  o.z = dot(M.row2, v);
  o.w = v.w; // often v.w==1 so this simplifies further
</pre>
<p>while with the column-major layout, you get to drop the last entry of every column vector, but that saves neither memory nor shader instructions. (As an aside, GL ES doesn&#8217;t support non-square matrix types directly; if you want to use a non-square matrix, you have to use an array/struct of vecs instead &#8211; another annoyance)</p>
<p>Furthermore, I generally prefer (for rendering code anyway) to store matrices in the format that I&#8217;m gonna send to the hardware or graphics API. On GL ES, that means I have to do one of three things:</p>
<ol>
<li>Use 4&#215;4 matrices everywhere and live with 25% unnecessary extra arithmetic and memory transfers,
<li>Have my 3&#215;4 matrix manipulation use row-major layout while 4&#215;4 uses column-major,
<li>Avoid the GL ES builtin mat4 type and use a vec4[4] (or a corresponding struct) instead.
</ol>
<p>Now, options 2 and 3 are perfectly workable, but they&#8217;re ugly, and it annoys me that an API that breaks compatibility with the original OpenGL in about 50 different ways anyway didn&#8217;t clean up this historical artifact.</p>
]]></html></oembed>