Éléments internes de bibliothèque

Cette rubrique décrit la conception interne de la bibliothèque DirectXMath.

Conventions d’appel

Pour améliorer la portabilité et optimiser la disposition des données, vous devez utiliser les conventions d’appel appropriées pour chaque plateforme prise en charge par la bibliothèque DirectXMath. Plus précisément, lorsque vous passez des objets XMVECTOR en tant que paramètres, qui sont définis comme étant alignés sur une limite de 16 octets, il existe différents ensembles d’exigences d’appel, selon la plateforme cible :

Pour Windows 32 bits

Pour Windows 32 bits, deux conventions d’appel sont disponibles pour le passage efficace des valeurs __m128 (qui implémente XMVECTOR sur cette plateforme). La norme est __fastcall, qui peut passer les trois premières valeurs __m128 (instances de XMVECTOR) en tant qu’arguments d’une fonction dans un registre SSE/SSE2. __fastcall passe les arguments restants via la pile.

Les compilateurs plus récents de Microsoft Visual Studio prennent en charge une nouvelle convention d’appel, __vectorcall, qui peut passer jusqu’à six valeurs __m128 (instances de XMVECTOR) en tant qu’arguments d’une fonction dans un registre SSE/SSE2. Il peut également passer des agrégats vectoriels hétérogènes (également connus sous le nom de XMMATRIX) via les registres SSE/SSE2 s’il y a suffisamment de place.

Pour les éditions 64 bits de Windows

Pour Windows 64 bits, deux conventions d’appel sont disponibles pour le passage efficace des valeurs __m128. La norme est __fastcall, qui passe toutes les valeurs __m128 sur la pile.

Les compilateurs Visual Studio plus récents prennent en charge la convention d’appel __vectorcall, qui peut passer jusqu’à six valeurs __m128 (instances de XMVECTOR) en tant qu’arguments d’une fonction dans un registre SSE/SSE2. Il peut également passer des agrégats vectoriels hétérogènes (également connus sous le nom de XMMATRIX) via les registres SSE/SSE2 s’il y a suffisamment de place.

Pour Windows sur ARM

Windows sur ARM & ARM64 prend en charge le passage des quatre premières valeurs __n128 (instances de XMVECTOR) en registre.

Solution DirectXMath

Les alias FXMVECTOR, GXMVECTOR, HXMVECTOR, et CXMVECTOR prennent en charge ces conventions :

  • Utilisez l’alias FXMVECTOR pour passer jusqu’aux trois premières instances de XMVECTOR utilisées comme arguments d’une fonction.
  • Utilisez l’alias GXMVECTOR pour passer la 4ème instance d’un XMVECTOR utilisé comme argument d’une fonction.
  • Utilisez l’alias HXMVECTOR pour passer les 5ème et 6ème instances d’un XMVECTOR utilisé comme argument d’une fonction. Pour plus d’informations sur les considérations supplémentaires, veuillez consulter la documentation de __vectorcall.
  • Utilisez l’alias CXMVECTOR pour passer toute autre instance de XMVECTOR utilisée comme argument.

Remarque

Pour les paramètres de sortie, utilisez toujours XMVECTOR* ou XMVECTOR& et ignorez-les par rapport aux règles précédentes pour les paramètres d’entrée.

 

En raison des limitations de __vectorcall, nous recommandons de ne pas utiliser GXMVECTOR ou HXMVECTOR pour les constructeurs C++. Utilisez simplement FXMVECTOR pour les trois premières valeurs XMVECTOR, puis utilisez CXMVECTOR pour le reste.

Les alias FXMMATRIX et CXMMATRIX aident à profiter du passage d’argument HVA avec __vectorcall.

  • Utilisez l’alias FXMMATRIX pour passer le premier XMMATRIX comme argument à la fonction. Cela suppose que vous n’avez pas plus de deux arguments FXMVECTOR ou plus de deux arguments float, double ou FXMVECTOR à la « droite » de la matrice. Pour plus d’informations sur les considérations supplémentaires, veuillez consulter la documentation de __vectorcall.
  • Sinon, utilisez l’alias CXMMATRIX.

En raison des limitations de __vectorcall, nous recommandons de ne jamais utiliser FXMMATRIX pour les constructeurs C++. Utilisez simplement CXMMATRIX.

En plus des alias de type, vous devez également utiliser l’annotation XM_CALLCONV pour vous assurer que la fonction utilise la convention d’appel appropriée (__fastcall versus __vectorcall) en fonction de votre compilateur et de votre architecture. En raison des limitations de __vectorcall, nous recommandons de ne pas utiliser XM_CALLCONV pour les constructeurs C++.

Voici quelques exemples de déclarations qui illustrent cette convention :

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);

Pour prendre en charge ces conventions d’appel, ces alias de type sont définis comme suit (les paramètres doivent être passés par valeur pour que le compilateur les considère pour le passage en registre) :

Pour les applications Windows 32 bits

Lorsque vous utilisez __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;

Lorsque vous utilisez __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;

Pour les applications Windows natives 64 bits

Lorsque vous utilisez __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;

Lorsque vous utilisez __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;

Windows sur ARM

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

Remarque

Bien que toutes les fonctions soient déclarées inline et que dans de nombreux cas le compilateur n’ait pas besoin d’utiliser les conventions d’appel pour ces fonctions, il existe des cas où le compilateur peut décider qu’il est plus efficace de ne pas inline la fonction et dans ces cas, nous voulons la meilleure convention d’appel possible pour chaque plateforme.

 

Équivalence des types de la bibliothèque graphique

Pour prendre en charge l’utilisation de la bibliothèque DirectXMath, de nombreux types et structures de la bibliothèque DirectXMath sont équivalents aux implémentations Windows des types D3DDECLTYPE et D3DFORMAT, ainsi que des types DXGI_FORMAT.

DirectXMath D3DDECLTYPE D3DFORMAT DXGI_FORMAT
XMBYTE2 DXGI_FORMAT_R8G8_SINT
XMBYTE4 D3DDECLTYPE_BYTE4 (Xbox seulement) D3DFMT_x8x8x8x8 DXGI_FORMAT_x8x8x8x8_SINT
XMBYTEN2 D3DFMT_V8U8 DXGI_FORMAT_R8G8_SNORM
XMBYTEN4 D3DDECLTYPE_BYTE4N (Xbox seulement) D3DFMT_x8x8x8x8 DXGI_FORMAT_x8x8x8x8_SNORM
XMCOLOR D3DDECLTYPE_D3DCOLOR D3DFMT_A8R8G8B8 DXGI_FORMAT_B8G8R8A8_UNORM (DXGI 1.1+)
XMDEC4 D3DDECLTYPE_DEC4 (Xbox seulement) D3DDECLTYPE_DEC3 (Xbox seulement)
XMDECN4 D3DDECLTYPE_DEC4N (Xbox seulement) D3DDECLTYPE_DEC3N (Xbox seulement)
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 (Utilisez XMLoadUDecN4_XR et XMStoreUDecN4_XR.)
XMUDEC4 D3DDECLTYPE_UDEC4 (Xbox seulement)
D3DDECLTYPE_UDEC3 (Xbox seulement)
D3DFMT_A2R10G10B10
D3DFMT_A2B10G10R10
DXGI_FORMAT_R10G10B10A2_UINT
XMUDECN4 D3DDECLTYPE_UDEC4N (Xbox seulement)
D3DDECLTYPE_UDEC3N (Xbox seulement)
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 seulement) D3DFMT_x16x16x16x16 DXGI_FORMAT_R16G16B16A16_UINT
XMUSHORTN4 D3DDECLTYPE_USHORT4N D3DFMT_x16x16x16x16 DXGI_FORMAT_R16G16B16A16_UNORM

 

Constantes globales dans la bibliothèque DirectXMath

Pour réduire la taille du segment de données, la bibliothèque DirectXMath utilise la macro XMGLOBALCONST pour utiliser un certain nombre de constantes internes globales dans son implémentation. Par convention, ces constantes globales internes sont préfixées par g_XM. En règle générale, elles sont l’un des types suivants : XMVECTORU32, XMVECTORF32, ou XMVECTORI32.

Ces constantes globales internes sont susceptibles de changer dans les futures révisions de la bibliothèque DirectXMath. Utilisez les fonctions publiques qui encapsulent les constantes lorsque cela est possible plutôt que l’utilisation directe des valeurs globales g_XM. Vous pouvez également déclarer vos propres constantes globales en utilisant XMGLOBALCONST.

Windows SSE Vs SSE2

L’ensemble d’instructions SSE ne prend en charge que les vecteurs en virgule flottante simple précision. DirectXMath doit utiliser l’ensemble d’instructions SSE2 pour fournir une prise en charge des vecteurs d’entiers. SSE2 est pris en charge par tous les processeurs Intel depuis l’introduction du Pentium 4, tous les processeurs AMD K8 et ultérieurs, et tous les processeurs compatibles x64.

Remarque

Windows 8 pour x86 ou ultérieur nécessite la prise en charge de SSE2. Toutes les versions de Windows x64 nécessitent la prise en charge de SSE2. Windows sur ARM / ARM64 nécessite ARM_NEON.

 

Variantes de routines

Il existe plusieurs variantes des fonctions DirectXMath qui facilitent votre travail :

  • Fonctions de comparaison pour créer une ramification conditionnelle complexe basée sur un plus petit nombre d’opérations de comparaison de vecteurs. Le nom de ces fonctions se termine par « R », comme XMVector3InBoundsR. Les fonctions renvoient un enregistrement de comparaison sous forme de valeur de retour UINT ou en tant que paramètre de sortie UINT. Vous pouvez utiliser les macros XMComparision* pour tester la valeur.
  • Fonctions batch pour effectuer des opérations de type batch sur des tableaux de vecteurs plus grands. Le nom de ces fonctions se termine par « Stream », comme XMVector3TransformStream. Les fonctions opèrent sur un tableau d’entrées et génèrent un tableau de sorties. En règle générale, elles prennent une foulée d’entrée et de sortie.
  • Fonctions d’estimation qui implémentent une estimation plus rapide au lieu d’un résultat plus lent et plus précis. Le nom de ces fonctions se termine par « Est », comme XMVector3NormalizeEst. L’impact sur la qualité et les performances de l’utilisation de l’estimation varie d’une plateforme à l’autre, mais nous vous recommandons d’utiliser les variantes d’estimation pour le code sensible aux performances.

Incohérences entre plateformes

La bibliothèque DirectXMath est destinée à être utilisée dans des applications graphiques sensibles aux performances et des jeux. Par conséquent, l’implémentation est conçue pour une vitesse optimale lors du traitement normal sur toutes les plateformes prises en charge. Les résultats dans des conditions limites, en particulier ceux qui génèrent des spécificités en virgule flottante, sont susceptibles de varier d’une cible à l’autre. Ce comportement dépendra également d’autres paramètres d’exécution, tels que le mot de contrôle x87 pour la cible Windows 32 bits sans intrinsics ou le mot de contrôle SSE pour les versions Windows 32 bits et 64 bits. De plus, il y aura des différences dans les conditions limites entre les différents fournisseurs de CPU.

N’utilisez pas DirectXMath dans des applications scientifiques ou autres où la précision numérique est primordiale. De plus, cette limitation se reflète dans l’absence de prise en charge des calculs à double ou autre précision étendue.

Remarque

Les chemins de code scalaire _XM_NO_INTRINSICS_ sont généralement écrits pour la conformité, pas pour les performances. Leurs résultats dans les conditions limites varieront également.

 

Extensions spécifiques à la plateforme

La bibliothèque DirectXMath est destinée à simplifier la programmation SIMD en C++ en fournissant un excellent support pour les plateformes x86, x64 et Windows RT en utilisant des instructions intrinsèques largement prises en charge (SSE2 et ARM-NEON).

Il y a cependant des moments où les instructions spécifiques à la plateforme peuvent s’avérer bénéfiques. En raison de la façon dont DirectXMath est implémenté, il est dans de nombreux cas trivial d’utiliser les types DirectXMath directement dans les instructions intrinsèques prises en charge par le compilateur standard, et d’utiliser DirectXMath comme chemin de secours pour les plateformes qui ne prennent pas en charge l’instruction étendue.

Par exemple, voici un exemple simplifié de l’utilisation de l’instruction de produit scalaire SSE 4.1. Notez que vous devez explicitement protéger le chemin de code pour éviter de générer des exceptions d’instruction non valides à l’exécution. Assurez-vous que les chemins de code effectuent un travail suffisant pour justifier le coût supplémentaire de la bifurcation, de la complexité de la maintenance de plusieurs chemins de code, etc.

#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 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;
}

Pour plus d’informations sur les extensions spécifiques à la plateforme, veuillez consulter :

DirectXMath : SSE, SSE2 et ARM-NEON
DirectXMath : SSE3 et SSSE3
DirectXMath : SSE4.1 et SSE4.2
DirectXMath : AVX
DirectXMath : F16C et FMA
DirectXMath : AVX2
DirectXMath : ARM64

Guide de programmation DirectXMath