NEON:如何将我的 SoA 4x 四元数到矩阵输出到非交错 4x4 矩阵数组?

NEON: How to I get my SoA 4x quaternion-to-matrix out to array of non-interleaved 4x4 matrices?

我仍在学习使用 NEON 的所有最佳方法,这就是我的问题。我有一个四元数到矩阵的操作,需要对四元数数组进行操作,然后添加平移以生成 4x4 矩阵。我将数据安排在 SOA 中,并使用一次对 4 个四元数进行操作的内在函数编写了以下内容

// two constants
float32x4_t one = { 1.f, 1.f, 1.f, 1.f };
float32x4_t two = { 2.f, 2.f, 2.f, 2.f };

// load the data 4 quaternions wide
float32x4_t rot_x = vld1q_f32(data->rotation_x); // load 4 quatenion's worth of x's
float32x4_t rot_y = vld1q_f32(data->rotation_y); // load 4 quatenion's worth of y's
float32x4_t rot_z = vld1q_f32(data->rotation_z); // load 4 quatenion's worth of z's
float32x4_t rot_w = vld1q_f32(data->rotation_w); // load 4 quatenion's worth of w's


float32x4_t qxx2 = vmulq_f32( vmulq_f32( rot_x, rot_x ), two );
float32x4_t qyy2 = vmulq_f32( vmulq_f32( rot_y, rot_y ), two );
float32x4_t qzz2 = vmulq_f32( vmulq_f32( rot_z, rot_z ), two );
float32x4_t qxy2 = vmulq_f32( vmulq_f32( rot_x, rot_y ), two );
float32x4_t qxz2 = vmulq_f32( vmulq_f32( rot_x, rot_z ), two );
float32x4_t qyz2 = vmulq_f32( vmulq_f32( rot_y, rot_z ), two );
float32x4_t qxw2 = vmulq_f32( vmulq_f32( rot_x, rot_w ), two );
float32x4_t qyw2 = vmulq_f32( vmulq_f32( rot_y, rot_w ), two );
float32x4_t qzw2 = vmulq_f32( vmulq_f32( rot_z, rot_w ), two );


float32x4_t m11 = vsubq_f32( one, vsubq_f32( qyy2, qzz2 ) );
float32x4_t m21 = vsubq_f32( qxy2, qzw2 );
float32x4_t m31 = vaddq_f32(qxz2, qyw2);

float32x4_t m12 = vaddq_f32(qxy2, qzw2);
float32x4_t m22 = vsubq_f32( one, vsubq_f32( qxx2, qzz2 ) );
float32x4_t m32 = vsubq_f32(qyz2, qxw2);

float32x4_t m13 = vsubq_f32( qxz2, qyw2 );
float32x4_t m23 = vaddq_f32( qyz2, qxw2);
float32x4_t m33 = vsubq_f32( one, vsubq_f32( qxx2, qyy2 ) );

这给了我 4 个 3x3 旋转矩阵。

最后我需要创建四个带平移的4x4矩阵,其中M14,M24,M34为0,平移存储在M41,M42,M43,M44为1.f。

struct Matrix
{
  float m11, m12, m13, m14;
  float m21, m22, m23, m24;
  float m31, m32, m33, m34;
  float m41, m42, m43, m44;
};

但我不知道如何有效地从 NEON 寄存器中提取数据。我试过简单地存储 NEON 寄存器中的数据然后对其进行操作,但显然性能很差。我想尽可能以最有效的方式包括翻译,但我不认为加载矢量只是为了存储它真的有帮助吗?

任何见解都会有所帮助。我在这里找什么?

不确定 NEON 是否有足够的向量寄存器,但您可以尝试这样的方法,未经测试:

inline float32x4_t unpackLow( float32x4_t a, float32x4_t b )
{
    float32x2_t x = vget_low_f32( a );
    float32x2_t y = vget_low_f32( b );
    return vcombine_f32( x, y );
}

inline float32x4_t unpackHigh( float32x4_t a, float32x4_t b )
{
    float32x2_t x = vget_high_f32( a );
    float32x2_t y = vget_high_f32( b );
    return vcombine_f32( x, y );
}

const float32x4_t zero = vdupq_n_f32( 0 );
const float32x4_t lastRow = vsetq_lane_f32( 1, zero, 3 );

// Zip them pairwise
const float32x4x2_t r11 = vzipq_f32( m11, m12 );
const float32x4x2_t r12 = vzipq_f32( m13, zero );
const float32x4x2_t r21 = vzipq_f32( m21, m22 );
const float32x4x2_t r22 = vzipq_f32( m23, zero );
const float32x4x2_t r31 = vzipq_f32( m31, m32 );
const float32x4x2_t r32 = vzipq_f32( m33, zero );

// Produce the matrices
vst1q_f32( pointer, unpackLow( r11.val[ 0 ], r12.val[ 0 ] ) );
vst1q_f32( pointer + 4, unpackLow( r21.val[ 0 ], r22.val[ 0 ] ) );
vst1q_f32( pointer + 8, unpackLow( r31.val[ 0 ], r32.val[ 0 ] ) );
vst1q_f32( pointer + 12, lastRow );

vst1q_f32( pointer + 16, unpackHigh( r11.val[ 0 ], r12.val[ 0 ] ) );
vst1q_f32( pointer + 20, unpackHigh( r21.val[ 0 ], r22.val[ 0 ] ) );
vst1q_f32( pointer + 24, unpackHigh( r31.val[ 0 ], r32.val[ 0 ] ) );
vst1q_f32( pointer + 28, lastRow );

vst1q_f32( pointer + 32, unpackLow( r11.val[ 1 ], r12.val[ 1 ] ) );
vst1q_f32( pointer + 36, unpackLow( r21.val[ 1 ], r22.val[ 1 ] ) );
vst1q_f32( pointer + 40, unpackLow( r31.val[ 1 ], r32.val[ 1 ] ) );
vst1q_f32( pointer + 44, lastRow );

vst1q_f32( pointer + 48, unpackHigh( r11.val[ 1 ], r12.val[ 1 ] ) );
vst1q_f32( pointer + 52, unpackHigh( r21.val[ 1 ], r22.val[ 1 ] ) );
vst1q_f32( pointer + 56, unpackHigh( r31.val[ 1 ], r32.val[ 1 ] ) );
vst1q_f32( pointer + 60, lastRow );

这些 vget_low / vget_high / vcombine 相对便宜,程序集有名称来寻址这些寄存器的 64 位部分。

如果由于寄存器不足而无法正常工作,第二个选项是 ZIP 只是中心元素,vzipq_f32( m11, m12 )vzipq_f32( m21, m22 )vzipq_f32( m31, m32 ),然后使用 vget_low / vget_high 对于它们,64 位向量存储即 vst1_f32 成对写入相应的矩阵元素,vst1q_lane_f32 存储从 m13、m23、m33 向量中提取的单个标量。