ライブラリの内部構造

このトピックでは、DirectXMath ライブラリの内部設計について説明します。

呼び出し規則

移植性を高め、データ レイアウトを最適化するには、DirectXMath ライブラリでサポートされているプラットフォームごとに適切な呼び出し規則を使用する必要があります。 具体的には、 XMVECTOR オブジェクトをパラメーターとして渡す場合(16 バイト境界にアラインされたものとして定義されます)、呼び出しの要件のセットは、ターゲット プラットフォームによって異なります。

32 ビット Windows の場合

32 ビット Windows の場合、 __m128 値を効率的に渡すために使用できる 2 つの呼び出し規則があります (この呼び出し規則は、そのプラットフォームに XMVECTOR を実装します)。 標準は __fastcallであり、最初の 3 つの __m128 値 (XMVECTOR インスタンス) を引数として SSE/SSE2 レジスタの関数に渡すことができます。 __fastcall は、スタックを介して残りの引数を渡します。

新しい Microsoft Visual Studio コンパイラでは、新しい呼び出し規則がサポートされています。__vectorcall、最大 6 つの __m128 値 (XMVECTOR インスタンス) を 引数として SSE/SSE2 レジスタの関数に渡すことができます。 また、十分なスペースがある場合は、SSE/SSE2 レジスタを介して異種ベクター集計 (XMMATRIX とも呼ばれます) を渡すこともできます。

64 ビット エディションの Windows の場合

64 ビット Windows の場合、 __m128 値を効率的に渡すために使用できる呼び出し規則が 2 つあります。 標準は __fastcallであり、スタック上のすべての __m128 値を渡します。

新しい Visual Studio コンパイラでは、__vectorcall呼び出し規則がサポートされています。これにより、SSE/SSE2 レジスタの関数に最大 6 つの__m128値 (XMVECTOR インスタンス) を引数として渡すことができます。 また、十分なスペースがある場合は、SSE/SSE2 レジスタを介して異種ベクター集計 (XMMATRIX とも呼ばれます) を渡すこともできます。

ARM 上の Windows の場合

ARM & ARM64 上の Windows では、最初の 4 つの__n128値 (XMVECTOR インスタンス) のインレジスタの受け渡しがサポートされています。

DirectXMath ソリューション

FXMVECTORGXMVECTORHXMVECTORCXMVECTOR の各エイリアスは、次の規則をサポートしています。

  • FXMVECTOR エイリアスを使用して、関数の引数として使用される XMVECTOR の最初の 3 つのインスタンスに渡します。
  • 引数として使用される XMVECTOR の 4 番目のインスタンスを関数に渡すには、GXMVECTOR エイリアスを使用します。
  • HXMVECTOR エイリアスを使用して、引数として使用される XMVECTOR の 5 番目と 6 番目のインスタンスを関数に渡します。 その他の考慮事項については、__vectorcallドキュメントを参照してください。
  • 引数として使用される XMVECTOR のそれ以上のインスタンスを渡すには 、CXMVECTOR エイリアスを使用します。

注意

出力パラメーターの場合は、必ず XMVECTOR* または XMVECTOR& を使用し、入力パラメーターに対する上記の規則に関しては無視してください。

 

__vectorcallに制限があるため、C++ コンストラクターには GXMVECTOR または HXMVECTOR を使用しないことをお勧めします。 最初の 3 つの XMVECTOR 値には FXMVECTOR を使用し、残りの値には CXMVECTOR を使用します。

FXMMATRIX エイリアスと CXMMATRIX エイリアスは、__vectorcallで渡す HVA 引数を利用するのに役立ちます。

  • 関数に引数として最初の XMMATRIX を渡すには、FXMMATRIX エイリアスを使用します。 これは、2 つ以上の FXMVECTOR 引数、または行列の "right" に対する 2 つ以上の float、double、または FXMVECTOR 引数がないことを前提としています。 その他の考慮事項については、__vectorcallドキュメントを参照してください。
  • それ以外の場合は、 CXMMATRIX エイリアスを使用します。

__vectorcallに制限があるため、C++ コンストラクターには FXMMATRIX を使用しないでください。 CXMMATRIX を使用するだけです。

型のエイリアスに加えて、XM_CALLCONV注釈を使用して、コンパイラとアーキテクチャに基づいて関数が適切な呼び出し規則 (__fastcall対__vectorcall) を使用するようにする必要もあります。 __vectorcallに制限があるため、C++ コンストラクターには XM_CALLCONV を使用しないことをお勧めします。

この規則を示す宣言の例を次に示します。

XMMATRIX XM_CALLCONV XMMatrixLookAtLH(FXMVECTOR EyePosition, FXMVECTOR FocusPosition, FXMVECTOR UpDirection);

XMMATRIX XM_CALLCONV XMMatrixTransformation2D(FXMVECTOR ScalingOrigin,  float ScalingOrientation, FXMVECTOR Scaling, FXMVECTOR RotationOrigin, float Rotation, GXMVECTOR Translation);

void XM_CALLCONV XMVectorSinCos(XMVECTOR* pSin, XMVECTOR* pCos, FXMVECTOR V);

XMVECTOR XM_CALLCONV XMVectorHermiteV(FXMVECTOR Position0, FXMVECTOR Tangent0, FXMVECTOR Position1, GXMVECTOR Tangent1, HXMVECTOR T);

XMMATRIX(FXMVECTOR R0, FXMVECTOR R1, FXMVECTOR R2, CXMVECTOR R3)

XMVECTOR XM_CALLCONV XMVector2Transform(FXMVECTOR V, FXMMATRIX M);

XMMATRIX XM_CALLCONV XMMatrixMultiplyTranspose(FXMMATRIX M1, CXMMATRIX M2);

これらの呼び出し規則をサポートするために、これらの型エイリアスは次のように定義されます (パラメーターは、コンパイラがインレジスタの受け渡しのためにそれらを考慮するために値で渡す必要があります)。

32 ビット Windows アプリの場合

__fastcallを使用する場合:

typedef const XMVECTOR  FXMVECTOR;
typedef const XMVECTOR& GXMVECTOR;
typedef const XMVECTOR& HXMVECTOR;
typedef const XMVECTOR& CXMVECTOR;
typedef const XMMATRIX& FXMMATRIX;
typedef const XMMATRIX& CXMMATRIX;

__vectorcallを使用する場合:

typedef const XMVECTOR  FXMVECTOR;
typedef const XMVECTOR  GXMVECTOR;
typedef const XMVECTOR  HXMVECTOR;
typedef const XMVECTOR& CXMVECTOR;
typedef const XMMATRIX  FXMMATRIX;
typedef const XMMATRIX& CXMMATRIX;

64 ビットネイティブ Windows アプリの場合

__fastcallを使用する場合:

typedef const XMVECTOR& FXMVECTOR;
typedef const XMVECTOR& GXMVECTOR;
typedef const XMVECTOR& HXMVECTOR;
typedef const XMVECTOR& CXMVECTOR;
typedef const XMMATRIX& FXMMATRIX;
typedef const XMMATRIX& CXMMATRIX;

__vectorcallを使用する場合:

typedef const XMVECTOR  FXMVECTOR;
typedef const XMVECTOR  GXMVECTOR;
typedef const XMVECTOR  HXMVECTOR;
typedef const XMVECTOR& CXMVECTOR;
typedef const XMMATRIX  FXMMATRIX;
typedef const XMMATRIX& CXMMATRIX;

ARM 版 Windows

typedef const XMVECTOR  FXMVECTOR;
typedef const XMVECTOR  GXMVECTOR;
typedef const XMVECTOR& CXMVECTOR;
typedef const XMMATRIX& FXMMATRIX;
typedef const XMMATRIX& CXMMATRIX;

注意

すべての関数はインラインで宣言されており、多くの場合、コンパイラはこれらの関数に対して呼び出し規則を使用する必要はありませんが、コンパイラが関数をインライン化しない方が効率的であると判断する場合があり、このような場合は各プラットフォームで可能な限り最高の呼び出し規則が必要です。

 

グラフィックス ライブラリの種類の等価性

DirectXMath ライブラリの使用をサポートするために、多くの DirectXMath ライブラリの型と構造体は、 D3DDECLTYPE 型と D3DFORMAT 型の Windows 実装と、 DXGI_FORMAT 型と同等です。

DirectXMath D3DDECLTYPE D3DFORMAT DXGI_FORMAT
XMBYTE2 DXGI_FORMAT_R8G8_SINT
XMBYTE4 D3DDECLTYPE_BYTE4 (Xbox のみ) D3DFMT_x8x8x8x8 DXGI_FORMAT_x8x8x8x8_SINT
XMBYTEN2 D3DFMT_V8U8 DXGI_FORMAT_R8G8_SNORM
XMBYTEN4 D3DDECLTYPE_BYTE4N (Xbox のみ) D3DFMT_x8x8x8x8 DXGI_FORMAT_x8x8x8x8_SNORM
XMCOLOR D3DDECLTYPE_D3DCOLOR D3DFMT_A8R8G8B8 DXGI_FORMAT_B8G8R8A8_UNORM (DXGI 1.1 以降)
XMDEC4 D3DDECLTYPE_DEC4 (Xbox のみ) D3DDECLTYPE_DEC3 (Xbox のみ)
XMDECN4 D3DDECLTYPE_DEC4N (Xbox のみ) D3DDECLTYPE_DEC3N (Xbox のみ)
XMFLOAT2 D3DDECLTYPE_FLOAT2 D3DFMT_G32R32F DXGI_FORMAT_R32G32_FLOAT
XMFLOAT2A D3DDECLTYPE_FLOAT2 D3DFMT_G32R32F DXGI_FORMAT_R32G32_FLOAT
XMFLOAT3 D3DDECLTYPE_FLOAT3 DXGI_FORMAT_R32G32B32_FLOAT
XMFLOAT3A D3DDECLTYPE_FLOAT3 DXGI_FORMAT_R32G32B32_FLOAT
XMFLOAT3PK DXGI_FORMAT_R11G11B10_FLOAT
XMFLOAT3SE DXGI_FORMAT_R9G9B9E5_SHAREDEXP
XMFLOAT4 D3DDECLTYPE_FLOAT4 D3DFMT_A32B32G32R32F DXGI_FORMAT_R32G32B32A32_FLOAT
XMFLOAT4A D3DDECLTYPE_FLOAT4 D3DFMT_A32B32G32R32F DXGI_FORMAT_R32G32B32A32_FLOAT
XMHALF2 D3DDECLTYPE_FLOAT16_2 D3DFMT_G16R16F DXGI_FORMAT_R16G16_FLOAT
XMHALF4 D3DDECLTYPE_FLOAT16_4 D3DFMT_A16B16G16R16F DXGI_FORMAT_R16G16B16A16_FLOAT
XMINT2 DXGI_FORMAT_R32G32_SINT
XMINT3 DXGI_FORMAT_R32G32B32_SINT
XMINT4 DXGI_FORMAT_R32G32B32A32_SINT
XMSHORT2 D3DDECLTYPE_SHORT2 D3DFMT_V16U16 DXGI_FORMAT_R16G16_SINT
XMSHORTN2 D3DDECLTYPE_SHORT2N D3DFMT_V16U16 DXGI_FORMAT_R16G16_SNORM
XMSHORT4 D3DDECLTYPE_SHORT4 D3DFMT_x16x16x16x16 DXGI_FORMAT_R16G16B16A16_SINT
XMSHORTN4 D3DDECLTYPE_SHORT4N D3DFMT_x16x16x16x16 DXGI_FORMAT_R16G16B16A16_SNORM
XMUBYTE2 DXGI_FORMAT_R8G8_UINT
XMUBYTEN2 D3DFMT_A8P8、D3DFMT_A8L8 DXGI_FORMAT_R8G8_UNORM
XMUINT2 DXGI_FORMAT_R32G32_UINT
XMUINT3 DXGI_FORMAT_R32G32B32_UINT
XMUINT4 DXGI_FORMAT_R32G32B32A32_UINT
XMU555 D3DFMT_X1R5G5B5、D3DFMT_A1R5G5B5 DXGI_FORMAT_B5G5R5A1_UNORM
XMU565 D3DFMT_R5G6B5 DXGI_FORMAT_B5G6R5_UNORM
XMUBYTE4 D3DDECLTYPE_UBYTE4 D3DFMT_x8x8x8x8 DXGI_FORMAT_x8x8x8x8_UINT
XMUBYTEN4 D3DDECLTYPE_UBYTE4N D3DFMT_x8x8x8x8 DXGI_FORMAT_x8x8x8x8_UNORM
DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM ( XMLoadUDecN4_XRXMStoreUDecN4_XRを使用します。
XMUDEC4 D3DDECLTYPE_UDEC4 (Xbox のみ)
D3DDECLTYPE_UDEC3 (Xbox のみ)
D3DFMT_A2R10G10B10
D3DFMT_A2B10G10R10
DXGI_FORMAT_R10G10B10A2_UINT
XMUDECN4 D3DDECLTYPE_UDEC4N (Xbox のみ)
D3DDECLTYPE_UDEC3N (Xbox のみ)
D3DFMT_A2R10G10B10
D3DFMT_A2B10G10R10
DXGI_FORMAT_R10G10B10A2_UNORM
XMUNIBBLE4 D3DFMT_A4R4G4B4、D3DFMT_X4R4G4B4 DXGI_FORMAT_B4G4R4A4_UNORM (DXGI 1.2 以降)
XMUSHORT2 D3DDECLTYPE_USHORT2 D3DFMT_G16R16 DXGI_FORMAT_R16G16_UINT
XMUSHORTN2 D3DDECLTYPE_USHORT2N D3DFMT_G16R16 DXGI_FORMAT_R16G16_UNORM
XMUSHORT4 D3DDECLTYPE_USHORT4 (Xbox のみ) D3DFMT_x16x16x16x16 DXGI_FORMAT_R16G16B16A16_UINT
XMUSHORTN4 D3DDECLTYPE_USHORT4N D3DFMT_x16x16x16x16 DXGI_FORMAT_R16G16B16A16_UNORM

 

DirectXMath ライブラリのグローバル定数

データ セグメントのサイズを小さくするために、DirectXMath ライブラリは XMGLOBALCONST マクロを使用して、その実装で多数のグローバル内部定数を使用します。 規則により、このような内部グローバル定数の前には g_XM が付けられます。 通常、これらは XMVECTORU32、XMVECTORF32、または XMVECTORI32 のいずれかの型です。

これらの内部グローバル定数は、DirectXMath ライブラリの将来のリビジョンで変更される可能性があります。 グローバル値を直接使用するのではなく、可能な限り定数をカプセル化するパブリック関数 g_XM 使用します。 XMGLOBALCONST を使用して独自のグローバル定数を宣言することもできます。

Windows SSE と SSE2

SSE 命令セットは、単精度浮動小数点ベクトルのサポートのみを提供します。 DirectXMath では、整数ベクトルのサポートを提供するために、SSE2 命令セットを使用する必要があります。 SSE2 は、Pentium 4、すべての AMD K8 以降のプロセッサ、およびすべての x64 対応プロセッサが導入されて以来、すべての Intel プロセッサでサポートされています。

注意

x86 以降のWindows 8には、SSE2 のサポートが必要です。 Windows x64 のすべてのバージョンでは、SSE2 のサポートが必要です。 ARM/ARM64 上の Windows にはARM_NEONが必要です。

 

ルーチンバリアント

DirectXMath 関数には、作業を簡単にするバリエーションがいくつかあります。

  • 比較関数を使用して、ベクター比較操作の数を減らした複雑な条件付き分岐を作成します。 これらの関数の名前は、XMVector3InBoundsR などの "R" で終わる。 関数は、UINT 戻り値として、または UINT out パラメーターとして比較レコードを返します。 XMComparision* マクロを使用して値をテストできます。
  • より大きなベクター配列に対してバッチ スタイルの操作を実行するためのバッチ関数。 これらの関数の名前は、 XMVector3TransformStream などの "Stream" で終わる。 関数は入力の配列に対して動作し、出力の配列を生成します。 通常、入力と出力のストライドを受け取ります。
  • より遅く、より正確な結果ではなく、より高速な推定を実装する推定関数。 これらの関数の名前は、 XMVector3NormalizeEst などの "Est" で終わる。 推定を使用した場合の品質とパフォーマンスへの影響はプラットフォームによって異なりますが、パフォーマンスに依存するコードには推定バリアントを使用することをお勧めします。

プラットフォームの不整合

DirectXMath ライブラリは、パフォーマンスに依存するグラフィックス アプリケーションやゲームでの使用を目的としています。 そのため、実装は、サポートされているすべてのプラットフォームで通常の処理を実行する最適な速度を実現するように設計されています。 境界条件での結果 (特に浮動小数点スペシャルを生成する結果) は、ターゲットごとに異なる可能性があります。 この動作は、Windows 32 ビット非組み込みターゲットの x87 制御ワードや、Windows 32 ビットと 64 ビットの両方の SSE 制御ワードなど、他の実行時設定にも依存します。 さらに、さまざまな CPU ベンダー間で境界条件に違いがあります。

数値精度が最も重要な科学やその他のアプリケーションでは DirectXMath を使用しないでください。 また、この制限は、倍精度計算やその他の拡張精度計算のサポートが不足している場合にも反映されます。

注意

一般に、_XM_NO_INTRINSICS_スカラー コード パスは、パフォーマンスではなく、コンプライアンスのために記述されます。 境界条件の結果も異なります。

 

プラットフォーム固有の拡張機能

DirectXMath ライブラリは、広範にサポートされている組み込み命令 (SSE2 と ARM-NEON) を使用して、x86、x64、およびWindows RT プラットフォームに優れたサポートを提供する C++ SIMD プログラミングを簡略化することを目的としています。

ただし、プラットフォーム固有の命令が有益であると証明される場合があります。 DirectXMath の実装方法により、多くの場合、標準のコンパイラでサポートされている組み込みステートメントで DirectXMath 型を直接使用し、拡張命令をサポートしていないプラットフォームのフォールバック パスとして DirectXMath を使用することは簡単です。

たとえば、SSE 4.1 ドット積命令を利用する簡単な例を次に示します。 実行時に無効な命令例外が生成されないように、コード パスを明示的に保護する必要があることに注意してください。 分岐の追加コスト、複数のコード パスの保守の複雑さを正当化するのに十分な作業をコード パスで行うことを確認します。

#include <Windows.h>
#include <stdio.h>

#include <DirectXMath.h>

#include <intrin.h>
#include <smmintrin.h>

using namespace DirectX;

bool g_bSSE41 = false;

void DetectCPUFeatures()
{
#ifndef _M_ARM
   // See __cpuid documentation on MSDN for more information

   int CPUInfo[4] = {-1};
#if defined(__clang__) || defined(__GNUC__)
   __cpuid(0, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]);
#else
   __cpuid(CPUInfo, 0);
#endif

   if ( CPUInfo[0] >= 1 )
   {
#if defined(__clang__) || defined(__GNUC__)
        __cpuid(1, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]);
#else
        __cpuid(CPUInfo, 1);
#endif

       if ( CPUInfo[2] & 0x80000 )
           g_bSSE41 = true;
   }
#endif
}

int main()
{
   if ( !XMVerifyCPUSupport() )
       return -1;

   DetectCPUFeatures();

   ...

   XMVECTORF32 v1 = { 1.f, 2.f, 3.f, 4.f };
   XMVECTORF32 v2 = { 5.f, 6.f, 7.f, 8.f };

   XMVECTOR r2, r3, r4;

   if ( g_bSSE41 )
   {
#ifndef _M_ARM
       r2 = _mm_dp_ps( v1, v2, 0x3f );
       r3 = _mm_dp_ps( v1, v2, 0x7f );
       r4 = _mm_dp_ps( v1, v2, 0xff );
#endif
   }
   else
   {
       r2 = XMVector2Dot( v1, v2 );
       r3 = XMVector3Dot( v1, v2 );
       r4 = XMVector4Dot( v1, v2 );
   }

   ...

   return 0;
}

プラットフォーム固有の拡張機能の詳細については、次を参照してください。

DirectXMath: SSE、SSE2、ARM-NEON
DirectXMath: SSE3 と SSSE3
DirectXMath: SSE4.1 と SSE4.2
DirectXMath: AVX
DirectXMath: F16C および FMA
DirectXMath: AVX2
DirectXMath: ARM64

DirectXMath プログラミング ガイド