Éléments internes de bibliothèque
Cette rubrique décrit la conception interne de la bibliothèque DirectXMath.
- Conventions d’appel
- Équivalence des types de la bibliothèque graphique
- Constantes globales dans la bibliothèque DirectXMath
- Windows SSE Vs SSE2
- Variantes de routines
- Incohérences entre plateformes
- Extensions spécifiques à la plateforme
- Rubriques connexes
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