É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 transmettez des objets XMVECTOR en tant que paramètres, qui sont définis comme alignés sur une limite de 16 octets, il existe différents ensembles d’exigences d’appel, en fonction de la plateforme cible :

Pour Windows 32 bits

Pour Windows 32 bits, deux conventions d’appel sont disponibles pour une transmission 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 XMVECTOR ) en tant qu’arguments à une fonction dans un registre SSE/SSE2 . __fastcall transmet les arguments restants via la pile.

Les compilateurs Microsoft Visual Studio plus récents prennent en charge une nouvelle convention d’appel, __vectorcall, qui peut transmettre jusqu’à six valeurs __m128 (instances XMVECTOR ) en tant qu’arguments à une fonction dans un registre SSE/SSE2 . Il peut également passer des agrégats vectoriels hétérogènes (également appelés XMMATRIX) via des 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 une transmission efficace des valeurs __m128 . La norme est __fastcall, qui transmet toutes les valeurs __m128 sur la pile.

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

Pour Windows sur ARM

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

Solution DirectXMath

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

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

Notes

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 vous 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 permettent de prendre en charge la prise en charge de la transmission de l’argument HVA avec __vectorcall.

  • Utilisez l’alias FXMMATRIX pour passer le premier XMMATRIX en tant qu’argument à la fonction. Cela suppose que vous n’avez pas plus de deux arguments FXMVECTOR ou plus de deux arguments float, double ou FXMVECTOR à droite de la matrice. Pour plus d’informations sur d’autres considérations, consultez la documentation __vectorcall.
  • Sinon, utilisez l’alias CXMMATRIX .

En raison des limitations de __vectorcall, nous vous 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 et __vectorcall) en fonction de votre compilateur et de votre architecture. En raison des limitations de __vectorcall, nous vous recommandons de ne pas utiliser XM_CALLCONV pour les constructeurs C++.

Voici des 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 comme étant passés dans le 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;

Notes

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

 

Équivalence du type de bibliothèque graphique

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

DirectXMath D3DDECLTYPE D3DFORMAT DXGI_FORMAT
XMBYTE2 DXGI_FORMAT_R8G8_SINT
XMBYTE4 D3DDECLTYPE_BYTE4 (Xbox uniquement) D3DFMT_x8x8x8x8 DXGI_FORMAT_x8x8x8x8_SINT
XMBYTEN2 D3DFMT_V8U8 DXGI_FORMAT_R8G8_SNORM
XMBYTEN4 D3DDECLTYPE_BYTE4N (Xbox uniquement) D3DFMT_x8x8x8x8 DXGI_FORMAT_x8x8x8x8_SNORM
XMCOLOR D3DDECLTYPE_D3DCOLOR D3DFMT_A8R8G8B8 DXGI_FORMAT_B8G8R8A8_UNORM (DXGI 1.1+)
XMDEC4 D3DDECLTYPE_DEC4 (Xbox uniquement) D3DDECLTYPE_DEC3 (Xbox uniquement)
XMDECN4 D3DDECLTYPE_DEC4N (Xbox uniquement) D3DDECLTYPE_DEC3N (Xbox uniquement)
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 (utiliser XMLoadUDecN4_XR et XMStoreUDecN4_XR.)
XMUDEC4 D3DDECLTYPE_UDEC4 (Xbox uniquement)
D3DDECLTYPE_UDEC3 (Xbox uniquement)
D3DFMT_A2R10G10B10
D3DFMT_A2B10G10R10
DXGI_FORMAT_R10G10B10A2_UINT
XMUDECN4 D3DDECLTYPE_UDEC4N (Xbox uniquement)
D3DDECLTYPE_UDEC3N (Xbox uniquement)
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 uniquement) 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, il s’agit de l’un des types suivants : XMVECTORU32, XMVECTORF32 ou XMVECTORI32.

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

Windows SSE et SSE2

Le jeu d’instructions SSE prend uniquement en charge les vecteurs à virgule flottante à précision unique. DirectXMath doit utiliser le jeu d’instructions SSE2 pour fournir la prise en charge des vecteurs entiers. SSE2 est pris en charge par tous les processeurs Intel depuis l’introduction du Pentium 4, de tous les processeurs AMD K8 et versions ultérieures, et de tous les processeurs compatibles x64.

Notes

Windows 8 pour x86 ou version ultérieure 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 routine

Il existe plusieurs variantes des fonctions DirectXMath qui facilitent l’exécution de votre travail :

  • Les fonctions de comparaison permettent de créer des branches conditionnelles complexes basées sur un plus petit nombre d’opérations de comparaison de vecteurs. Le nom de ces fonctions se termine par « R », par exemple XMVector3InBoundsR. Les fonctions retournent un enregistrement de comparaison sous la forme d’une valeur de retour UINT ou d’un 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 vectoriels plus volumineux. Le nom de ces fonctions se termine par « Stream », comme XMVector3TransformStream. Les fonctions fonctionnent sur un tableau d’entrées et génèrent un tableau de sorties. En règle générale, ils 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 des variantes d’estimation pour le code sensible aux performances.

Incohérences de la plateforme

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

N’utilisez pas DirectXMath dans des applications scientifiques ou autres où la précision numérique est primordiale. En outre, cette limitation se reflète dans le manque de prise en charge des calculs de précision double ou d’autres calculs de précision étendus.

Notes

Les chemins de code scalaires _XM_NO_INTRINSICS_ sont généralement écrits pour la conformité, et non pour les performances. Leurs résultats de condition de limite varient également.

 

Extensions spécifiques à la plateforme

La bibliothèque DirectXMath est destinée à simplifier la programmation SIMD C++ offrant une excellente prise en charge des plateformes x86, x64 et Windows RT à l’aide d’instructions intrinsèques largement prises en charge (SSE2 et ARM-NEON).

Toutefois, il arrive parfois que des instructions spécifiques à la plateforme s’avèrent utiles. En raison de la façon dont DirectXMath est implémenté, il est souvent trivial d’utiliser des types DirectXMath directement dans des instructions intrinsèques standard prises en charge par le compilateur et d’utiliser DirectXMath comme chemin d’accès de secours pour les plateformes qui ne prennent pas en charge l’instruction étendue.

Par exemple, voici un exemple simplifié d’utilisation de l’instruction dot-product SSE 4.1. Notez que vous devez protéger explicitement le chemin du code pour éviter de générer des exceptions d’instruction non valides au moment de l’exécution. Assurez-vous que les chemins de code effectuent suffisamment de travail pour justifier le coût supplémentaire de la branche, la complexité de la maintenance de plusieurs chemins de code, et ainsi de suite.

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

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

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