Besonderheiten von Bibliotheken

In diesem Thema wird der interne Entwurf der DirectXMath-Bibliothek beschrieben.

Aufrufkonventionen

Um die Portabilität zu verbessern und das Datenlayout zu optimieren, müssen Sie die entsprechenden Aufrufkonventionen für jede Plattform verwenden, die von der DirectXMath-Bibliothek unterstützt wird. Insbesondere wenn Sie XMVECTOR-Objekte als Parameter übergeben, die als an einer 16-Byte-Grenze ausgerichtet definiert sind, gibt es je nach Zielplattform unterschiedliche Aufrufanforderungen:

Für 32-Bit-Windows

Für 32-Bit-Windows stehen zwei Aufrufkonventionen für die effiziente Übergabe von __m128-Werten (die XMVECTOR auf dieser Plattform implementiert) zur Verfügung. Der Standard ist __fastcall, der die ersten drei __m128-Werte (XMVECTOR-Instanzen ) als Argumente an eine Funktion in einem SSE/SSE2-Register übergeben kann. __fastcall die verbleibenden Argumente über den Stapel übergeben.

Neuere Microsoft Visual Studio-Compiler unterstützen eine neue Aufrufkonvention, __vectorcall, die bis zu sechs __m128-Werte (XMVECTOR-Instanzen ) als Argumente an eine Funktion in einem SSE/SSE2-Register übergeben kann. Es kann auch heterogene Vektoraggregate (auch bekannt als XMMATRIX) über SSE/SSE2-Register übergeben, wenn genügend Platz vorhanden ist.

Für 64-Bit-Editionen von Windows

Für 64-Bit-Windows stehen zwei Aufrufkonventionen zur effizienten Übergabe von __m128 Werten zur Verfügung. Der Standard ist __fastcall, der alle __m128 Werte auf dem Stapel übergibt.

Neuere Visual Studio-Compiler unterstützen die __vectorcall Aufrufkonvention, die bis zu sechs __m128-Werte (XMVECTOR-Instanzen ) als Argumente an eine Funktion in einem SSE/SSE2-Register übergeben kann. Es kann auch heterogene Vektoraggregate (auch bekannt als XMMATRIX) über SSE/SSE2-Register übergeben, wenn genügend Platz vorhanden ist.

Für Windows auf ARM

Windows on ARM64 & unterstützt das Übergeben der ersten vier __n128-Werte (XMVECTOR-Instanzen ) in der Registrierung.

DirectXMath-Lösung

Die Aliase FXMVECTOR, GXMVECTOR, HXMVECTOR und CXMVECTOR unterstützen die folgenden Konventionen:

  • Verwenden Sie den FXMVECTOR-Alias , um die ersten drei XMVECTOR-Instanzen zu übergeben, die als Argumente für eine Funktion verwendet werden.
  • Verwenden Sie den GXMVECTOR-Alias, um den 4. instance eines als Argument verwendeten XMVECTOR an eine Funktion zu übergeben.
  • Verwenden Sie den HXMVECTOR-Alias , um die 5. und 6. Instanz eines als Argument verwendeten XMVECTOR an eine Funktion zu übergeben. Weitere Informationen zu weiteren Überlegungen finden Sie in der __vectorcall-Dokumentation.
  • Verwenden Sie den CXMVECTOR-Alias , um alle weiteren XMVECTOR-Instanzen zu übergeben, die als Argumente verwendet werden.

Hinweis

Verwenden Sie für Ausgabeparameter immer XMVECTOR* oder XMVECTOR& , und ignorieren Sie diese in Bezug auf die vorherigen Regeln für Eingabeparameter.

 

Aufgrund der Einschränkungen mit __vectorcall wird empfohlen, keine GXMVECTOR - oder HXMVECTOR-Konstruktoren für C++-Konstruktoren zu verwenden. Verwenden Sie einfach FXMVECTOR für die ersten drei XMVECTOR-Werte , und verwenden Sie dann CXMVECTOR für den Rest.

Die Aliase FXMMATRIX und CXMMATRIX unterstützen die Nutzung des HVA-Arguments, das an __vectorcall übergeben wird.

  • Verwenden Sie den FXMMATRIX-Alias , um die erste XMMATRIX als Argument an die Funktion zu übergeben. Dies setzt voraus, dass Sie nicht mehr als zwei FXMVECTOR-Argumente oder mehr als zwei Float-, Double- oder FXMVECTOR-Argumente "rechts" der Matrix haben. Weitere Informationen zu weiteren Überlegungen finden Sie in der __vectorcall-Dokumentation.
  • Verwenden Sie andernfalls den CXMMATRIX-Alias .

Aufgrund der Einschränkungen mit __vectorcall wird empfohlen, FXMMATRIX für C++-Konstruktoren niemals zu verwenden. Verwenden Sie einfach CXMMATRIX.

Zusätzlich zu den Typalias müssen Sie auch die XM_CALLCONV Anmerkung verwenden, um sicherzustellen, dass die Funktion die entsprechende Aufrufkonvention (__fastcall im Vergleich zu __vectorcall) basierend auf Ihrem Compiler und Ihrer Architektur verwendet. Aufgrund von Einschränkungen bei __vectorcall wird empfohlen, keine XM_CALLCONV für C++-Konstruktoren zu verwenden.

Die folgenden Beispieldeklarationen veranschaulichen diese Konvention:

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

Um diese Aufrufkonventionen zu unterstützen, werden diese Typalias wie folgt definiert (Parameter müssen als Wert übergeben werden, damit der Compiler sie für die Registerübergabe berücksichtigen kann):

Für 32-Bit-Windows-Apps

Wenn Sie __fastcall verwenden:

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

Wenn Sie __vectorcall verwenden:

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

Für native 64-Bit-Windows-Apps

Wenn Sie __fastcall verwenden:

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

Wenn Sie __vectorcall verwenden:

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 auf ARM

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

Hinweis

Während alle Funktionen inline deklariert sind und der Compiler in vielen Fällen keine Aufrufkonventionen für diese Funktionen verwenden muss, gibt es Fälle, in denen der Compiler möglicherweise entscheidet, dass es effizienter ist, die Funktion nicht inline zu integrieren, und in diesen Fällen möchten wir die bestmögliche Aufrufkonvention für jede Plattform.

 

Äquivalenz des Grafikbibliothekstyps

Um die Verwendung der DirectXMath-Bibliothek zu unterstützen, entsprechen viele DirectXMath-Bibliothekstypen und -Strukturen den Windows-Implementierungen der Typen D3DDECLTYPE und D3DFORMAT sowie den DXGI_FORMAT-Typen .

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

 

Globale Konstanten in der DirectXMath-Bibliothek

Um die Größe des Datensegments zu reduzieren, verwendet die DirectXMath-Bibliothek das XMGLOBALCONST-Makro , um eine Reihe von globalen internen Konstanten bei der Implementierung zu verwenden. Gemäß der Konvention werden solche internen globalen Konstanten durch g_XM präfixiert. In der Regel handelt es sich um einen der folgenden Typen: XMVECTORU32, XMVECTORF32 oder XMVECTORI32.

Diese internen globalen Konstanten können sich in zukünftigen Überarbeitungen der DirectXMath-Bibliothek ändern. Verwenden Sie öffentliche Funktionen, die die Konstanten nach Möglichkeit kapseln, anstatt g_XM globalen Werte direkt zu verwenden. Sie können auch Ihre eigenen globalen Konstanten mit XMGLOBALCONST deklarieren.

Windows SSE im Vergleich zu SSE2

Der SSE-Befehlssatz bietet nur Unterstützung für Gleitkommavektoren mit einzeler Genauigkeit. DirectXMath muss den SSE2-Anweisungssatz verwenden, um ganzzahlige Vektorunterstützung bereitzustellen. SSE2 wird seit der Einführung des Pentium 4 von allen Intel-Prozessoren, allen AMD K8- und höher-Prozessoren und allen x64-fähigen Prozessoren unterstützt.

Hinweis

Windows 8 für x86 oder höher erfordert Unterstützung für SSE2. Alle Versionen von Windows x64 erfordern Unterstützung für SSE2. Windows auf ARM/ARM64 erfordert ARM_NEON.

 

Routinevarianten

Es gibt mehrere Varianten von DirectXMath-Funktionen, die Ihre Arbeit erleichtern:

  • Vergleichsfunktionen, um komplizierte bedingte Verzweigungen basierend auf einer geringeren Anzahl von Vektorvergleichsvorgängen zu erstellen. Der Name dieser Funktionen endet auf "R", z. B. XMVector3InBoundsR. Die Funktionen geben einen Vergleichsdatensatz als UINT-Rückgabewert oder als UINT-Out-Parameter zurück. Sie können die XMComparision* -Makros verwenden, um den Wert zu testen.
  • Batchfunktionen zum Ausführen von Batchvorgängen für größere Vektorarrays. Der Name dieser Funktionen endet in "Stream", z. B. XMVector3TransformStream. Die Funktionen arbeiten mit einem Array von Eingaben und generieren ein Array von Ausgaben. In der Regel nehmen sie einen Eingabe- und Ausgabeschritt an.
  • Schätzfunktionen, die eine schnellere Schätzung anstelle eines langsameren, genaueren Ergebnisses implementieren. Der Name dieser Funktionen endet in "Est", z. B. XMVector3NormalizeEst. Die Auswirkungen der Verwendung von Schätzung auf Qualität und Leistung variieren von Plattform zu Plattform. Es wird jedoch empfohlen, Schätzvarianten für leistungsabhängigen Code zu verwenden.

Plattforminkonsistenzen

Die DirectXMath-Bibliothek ist für die Verwendung in leistungsabhängigen Grafikanwendungen und Spielen vorgesehen. Daher ist die Implementierung für optimale Geschwindigkeit bei normaler Verarbeitung auf allen unterstützten Plattformen konzipiert. Ergebnisse bei Begrenzungsbedingungen, insbesondere bei denen, die Gleitkomma-Specials generieren, variieren wahrscheinlich von Ziel zu Ziel. Dieses Verhalten hängt auch von anderen Laufzeiteinstellungen ab, z. B. dem x87-Steuerelementwort für das Windows 32-Bit-Ziel ohne systeminterne Einstellungen oder dem SSE-Steuerelementwort für Windows 32-Bit und 64-Bit. Darüber hinaus wird es Unterschiede in den Begrenzungsbedingungen zwischen verschiedenen CPU-Anbietern geben.

Verwenden Sie DirectXMath nicht in wissenschaftlichen oder anderen Anwendungen, in denen numerische Genauigkeit von größter Bedeutung ist. Diese Einschränkung spiegelt sich auch in der fehlenden Unterstützung für Berechnungen mit doppelter oder anderer erweiterter Genauigkeit wider.

Hinweis

Die _XM_NO_INTRINSICS_ skalaren Codepfade werden in der Regel aus Gründen der Konformität und nicht der Leistung geschrieben. Die Ergebnisse der Begrenzungsbedingung variieren ebenfalls.

 

Plattformspezifische Erweiterungen

Die DirectXMath-Bibliothek soll die C++-SIMD-Programmierung vereinfachen und bietet hervorragende Unterstützung für x86-, x64- und Windows RT-Plattformen mithilfe von allgemein unterstützten intrinsischen Anweisungen (SSE2 und ARM-NEON).

Es gibt jedoch Zeiten, in denen sich plattformspezifische Anweisungen als vorteilhaft erweisen können. Aufgrund der Art und Weise, wie DirectXMath implementiert wird, ist es in vielen Fällen trivial, DirectXMath-Typen direkt in standardmäßigen compilergestützten Systemanweisungen zu verwenden und DirectXMath als Fallbackpfad für Plattformen zu verwenden, die die erweiterte Anweisung nicht unterstützen.

Hier sehen Sie beispielsweise ein vereinfachtes Beispiel für die Nutzung der SSE 4.1-Dot-Product-Anweisung. Beachten Sie, dass Sie den Codepfad explizit schützen müssen, um zu vermeiden, dass zur Laufzeit ungültige Anweisungsausnahmen generiert werden. Stellen Sie sicher, dass die Codepfade erheblich genug Arbeit leisten, um die zusätzlichen Kosten für verzweigen, die Komplexität der Verwaltung mehrerer Codepfade usw. zu rechtfertigen.

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

Weitere Informationen zu plattformspezifischen Erweiterungen finden Sie unter:

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

DirectXMath-Programmierhandbuch