In my other article, vectors in DirectXMath, I discussed some of the aspects of vectors in DirectXMath. This article will continue the discussion of matrices in DirectXMath, including matrix types, compiler optimizations, and common function operations. (Some of the compiler issues discussed in Vectors in DirectXMath will not be covered here. Refer to the previous article if necessary).

Matrix type

In DirectXMath, we use the XMMATRIX class to represent a 4 × 4 matrix (typically a 4 × 4 matrix for operations in graphics) as follows:

#if (defined(_M_IX86)|| defined(_M_X64) || defined(_M_ARM)) && defined(_XM_NO_INTRINSICS_)
struct XMMATRIX
#else
__declspec(align(16)) struct XMMATRIX
#endif
{
    // Use 4 XMVECTORs to represent the matrix for SIMD.
    XMVECTOR r[4];
    
    XMMATRIX() {}
    
    // Initialize matrix by specifying 4 row vectors.
    XMMATRIX(FXMVECTOR R0, FXMVECTOR R1, FXMVECTOR R2, CXMVECTOR R3) {
        r[0] = R0; r[1] = R1; r[2] = R2; r[3] = R3; 
    }
    
    // Initialize matrix by specifying 4 row vectors.
    XMMATRIX(
        float m00, float m01, float m02, float m03,
        float m10, float m11, float m12, float m13,
        float m20, float m21, float m22, float m23,
        float m30, float m31, float m32, float m33
    );
    
    // Pass array of sixteen floats to construct matrix.
    explicit XMMATRIX(_In_reads(16) const float *pArray);
    
    XMMATRIX& operator= (const XMMATRIX &M) {
        r[0] = M.r[0]; r[1] = M.r[1]; 
        r[2] = M.r[2]; r[3] = M.r[3];
        return *this;
    }
    
    XMMATRIX operator+ () const { return *this; }
    XMMATRIX operator- () const;
    
    XMMATRIX& XM_CALLCONV operator+= (FXMMATRIX M);
    XMMATRIX& XM_CALLCONV operator-= (FXMMATRIX M);
    XMMATRIX& XM_CALLCONV operator*= (FXMMATRIX M);
    XMMATRIX& operator*= (float S);
    XMMATRIX& operator/= (float S);
    
    XMMATRIX XM_CALLCONV operator+ (FXMMATRIX M) const;
    XMMATRIX XM_CALLCONV operator- (FXMMATRIX M) const;
    XMMATRIX XM_CALLCONV operator* (FXMMATRIX M) const;
    XMMATRIX operator* (float S) const;
    XMMATRIX operator/ (float S) const;
    
    friend XMMATRIX XM_CALLCONV operator* (float S, FXMMATRIX M);
};

XMMATRIX XM_CALLCONV XMMatrixSet(
    float m00, float m01, float m02, float m03,
    float m10, float m11, float m12, float m13,
    float m20, float m21, float m22, float m23,
    float m30, float m31, float m32, float m33
);
Copy the code

You can see that the XMMATRIX type contains four XMVECTOR instances to make use of SIMD. XMMATRIX overloads a series of operators to perform the corresponding matrix operations. Alternatively, we can create an XMMATRIX instance by calling the XMMatrixSet function.

Similar to the XMVECTOR type, memory alignment is automatically implemented by the compiler when XMMATRIX instances are used as global and local variables, and memory alignment problems can occur when XMMATRIX instances are used as data members of classes. Therefore, it is generally recommended to use the XMFLOAT4X4 type to store matrix instances in the data members of the class, as defined below:

struct XMFLOAT4X4 {
    union {
        struct {
            float _11, _12, _13, _14;
            float _21, _22, _23, _24;
            float _31, _32, _33, _34;
            float _41, _42, _43, _44;
        };
        float m[4][4];
    };
    
    XMFLOAT4X4 () {}
    XMFLOAT4X4(
        float m00, float m01, float m02, float m03,
        float m10, float m11, float m12, float m13,
        float m20, float m21, float m22, float m23,
        float m30, float m31, float m32, float m33
    );
    explicit XMFLOAT4X4(_In_reads(16) const float *pArray);
    
    float operator() (size_t Row, size_t Column) const {
        return m[Row][Column];
    }
    float& operator() (size_t Row, size_t Column) {
        return m[Row][Column];
    }
    
    XMFLOAT4X4& operator= (const XMFLOAT4X4 &Float4x4);
};
Copy the code

Similarly, DirectXMath provides Loading and Storage methods for converting from XMMATRIX to XMFLOAT4X4:

// Loading methods: load data from XMFLOAT4X4 into XMMATRIX
inline XMMATRIX XM_CALLCONV XMLoadFloat4x4(const XMFLOAT4X4 *pSource);

// // Stroing methods: store XMMATRIX into XMFLOAT4X4
inline void XM_CALLCONV XMStoreFloat4x4(XMFLOAT4X4 *pDestination, FXMMATRIX M);
Copy the code

Function parameter passing and calling convention

Similar to XMVECTOR, in order to improve efficiency, when the XMMATRIX value is passed as an argument to a function in a function call, it can be passed directly to the SSE/SSE2 register rather than stored in stack memory. The difference is that arguments of type XMMATRIX count as four XMVECTOR arguments. The use of the XMMATRIX parameter type alias follows these rules:

  • When the argument list of a functionXMVECTORIf the number of type parameters does not exceed two, the first parameter is specifiedXMMATRIXParameters should be usedFXMMATRIXType, the restXMMATRIXParameters usingCXMMATRIXType;
  • When the argument list of a functionXMVECTORType more than two parameters when allXMMATRIXAll parameters are usedCXMMATRIXType.

The reasons for adopting the above rules are a little more subtle: As mentioned in Vectors in DirectXMath, a maximum of six XMVECTOR arguments can be passed to SSE/SSE2 registers. When the number of XMVECTOR arguments is not more than two, After excluding the SSE/SSE2 register occupied by the XMVECTOR parameter, there may be up to 4 SSE/SSE2 registers that can accommodate the next XMMATRIX parameter, so the first XMMATRIX parameter can use the FXMMATRIX type. When the number of XMVECTOR parameters exceeds two, the remaining SSE/SSE2 registers are not enough to hold one XMMATRIX parameter, so all XMMATRIX parameters use CXMMATRIX type.

Also, for a discussion of the modifiers XM_CALLCONV that call Conventions, see Vectors in DirectXMath.

In fact, FXMMATRIX and other type aliases in different platforms and compiler implementation is different, for parameter passing and call convention details and detailed rules, interested can go to the official documentation provided by Microsoft.

Commonly used matrix functions

DirectXMath provides many functions to perform matrix-related operations. Here are some common matrix functions:

XMMATRIX XM_CALLCONV XMMatrixIdentity(); // Returns the identity matrix I bool XM_CALLCONV XMMatrixIsIdentity( // Returns true if M is the identity matrix FXMMATRIX M // Input M ); XMMATRIX XM_CALLCONV XMMatrixMultiply( // Returns the matrix product AB FXMMATRIX A, // Input A CXMMATRIX B // Input B ); XMMATRIX XM_CALLCONV XMMatrixTranspose( // Returns M^T FXMMATRIX M // Input M ); XMVECTOR XM_CALLCONV XMMatrixDeterminant( // Returns (det M, det M, det M, det M) FXMMATRIX M // Input M ); Degradation of MATRIX XM_CALLCONV XMMatrixInverse(// Returns M^−1 XMVECTOR* pDeterminant, // Input (determinant M, determinant M, determinant M, det M) FXMMATRIX M // Input M );Copy the code

conclusion

This paper first discusses the matrix types XMMATRIX and XMFLOAT4X4 in DirectXMath, and then discusses some problems of function parameter transfer and call convention, and finally introduces some common matrix operation functions provided by DirectXMath.

reference