Bibliotheksinterna

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 zur Verfügung (die XMVECTOR auf dieser Plattform implementiert). 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 übergibt verbleibende Argumente über den Stapel.

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. Sie kann auch heterogene Vektoraggregate (auch als XMMATRIXbezeichnet) über SSE/SSE2-Register übergeben, wenn ausreichend Platz vorhanden ist.

Für 64-Bit-Editionen von Windows

Für 64-Bit-Windows stehen zwei Aufrufkonventionen für die effiziente Ü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. Sie kann auch heterogene Vektoraggregate (auch als XMMATRIXbezeichnet) über SSE/SSE2-Register übergeben, wenn ausreichend Platz vorhanden ist.

Für Windows auf ARM

Die Windows auf ARM & ARM64 unterstützt die Übergabe der ersten vier _ _ n128-Werte (XMVECTOR-Instanzen) im Register.

DirectXMath-Lösung

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

  • Verwenden Sie den FXMVECTOR-Alias, um bis zu den ersten drei XMVECTOR-Instanzen zu übergeben, die als Argumente für eine Funktion verwendet werden.
  • Verwenden Sie den GXMVECTOR-Alias, um die 4. Instanz eines XMVECTOR als Argument an eine Funktion zu übergeben.
  • Verwenden Sie den HXMVECTOR-Alias, um die 5. und 6. Instanz eines XMVECTOR als Argument an eine Funktion zu übergeben. Weitere Informationen zu zusätzlichen Ü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 sie in Bezug auf die oben genannten Regeln für Eingabeparameter.

Aufgrund von Einschränkungen bei _ _ vectorcall wird empfohlen, GXMVECTOR oder HXMVECTOR nicht 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 FXMMATRIX- und CXMMATRIX-Aliase unterstützen die Nutzung des HVA-Arguments, das mit 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 auf der rechten Seite der Matrix haben. Weitere Informationen zu zusätzlichen Überlegungen finden Sie in der _ _ Vectorcall-Dokumentation.
  • Verwenden Sie andernfalls den CXMMATRIX-Alias.

Aufgrund von Einschränkungen bei _ _ vectorcall wird empfohlen, FXMMATRIX niemals für C++-Konstruktoren zu verwenden. Verwenden Sie einfach CXMMATRIX.

Zusätzlich zu den Typaliasen 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, XM _ CALLCONV nicht für C++-Konstruktoren zu verwenden.

Im Folgenden sind Beispieldeklarationen dargestellt, die diese Konvention veranschaulichen:

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 Typaliase wie folgt definiert (Parameter müssen als Wert übergeben werden, damit der Compiler sie für die Registerübergabe berücksichtigt):

Für 32-Bit-Windows-Apps

Bei Verwendung von _ _ 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;

Bei Verwendung von _ _ 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;

Für native 64-Bit-Windows-Apps

Bei Verwendung von _ _ 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;

Bei Verwendung von _ _ 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 auf ARM

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

Hinweis

Obwohl 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 entscheiden kann, dass es effizienter ist, die Funktion nicht inline zu setzen, und in diesen Fällen möchten wir die bestmögliche Aufrufkonvention für jede Plattform verwenden.

Ä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 der 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 (XMLoadUDecN4 _ XR und XMStoreUDecN4 _ XRverwenden.)
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 in ihrer Implementierung zu verwenden. Standardmäßig wird solchen internen globalen Konstanten g _ XM vorangestellt. In der Regel handelt es sich um einen der folgenden Typen: XMVECTORU32, XMVECTORF32oder XMVECTORI32.

Diese internen globalen Konstanten können in zukünftigen Revisionen der DirectXMath-Bibliothek geändert werden. Verwenden Sie öffentliche Funktionen, die die Konstanten nach Möglichkeit kapseln, anstatt globale g _ XM-Werte direkt zu verwenden. Sie können auch ihre eigenen globalen Konstanten mit XMGLOBALCONSTdeklarieren.

Windows SSE im Vergleich zu SSE2

Der SSE-Anweisungssatz bietet nur Unterstützung für Gleitkommavektoren mit einfacher Genauigkeit. DirectXMath muss den SSE2-Anweisungssatz verwenden, um Ganzzahlvektorunterstützung bereitzustellen. SSE2 wird von allen Intel-Prozessoren seit der Einführung von Pentium 4, 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 ihnen die Arbeit erleichtern:

  • Vergleichsfunktionen zum Erstellen komplizierter bedingter Verzweigungen basierend auf einer kleineren Anzahl von Vektorvergleichsvorgängen. 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 auf "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 vor.
  • Schätzfunktionen, die anstelle eines langsameren, genaueren Ergebnisses eine schnellere Schätzung implementieren. Der Name dieser Funktionen endet auf "Est", z.B. XMVector3NormalizeEst. Die Auswirkungen der 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 eine optimale Geschwindigkeit bei der normalen Verarbeitung auf allen unterstützten Plattformen konzipiert. Ergebnisse bei Begrenzungsbedingungen, insbesondere solche, die Gleitkommaspezialitäten generieren, variieren wahrscheinlich von Ziel zu Ziel. Dieses Verhalten hängt auch von anderen Laufzeiteinstellungen ab, z. B. dem x87-Steuerwort für das Windows 32-Bit-Ziel ohne systeminterne Funktionen oder dem SSE-Steuerwort für Windows 32-Bit und 64-Bit. Darüber hinaus gibt es Unterschiede bei den Begrenzungsbedingungen zwischen verschiedenen CPU-Anbietern.

Verwenden Sie DirectXMath nicht in wissenschaftlichen oder anderen Anwendungen, in denen die numerische Genauigkeit von entscheidender 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-Skalarcodepfade _ werden im Allgemeinen aus Kompatibilitätsgründen und nicht aus Leistungsgründen geschrieben. Die Ergebnisse der Begrenzungsbedingungen variieren ebenfalls.

Plattformspezifische Erweiterungen

Die DirectXMath-Bibliothek soll die C++-SIMD-Programmierung vereinfachen und eine hervorragende Unterstützung für x86-, x64- und Windows RT-Plattformen bieten, indem allgemein unterstützte systeminterne Anweisungen (SSE2 und ARM-NEON) verwendet werden.

Es gibt jedoch Zeiten, in denen plattformspezifische Anweisungen sich 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 vom Compiler unterstützten systeminternen Standardanweisungen zu verwenden und DirectXMath als Fallbackpfad für Plattformen zu verwenden, die die erweiterte Anweisung nicht unterstützen.

Hier ist beispielsweise ein vereinfachtes Beispiel für die Nutzung der Punktproduktanweisung SSE 4.1. Beachten Sie, dass Sie den Codepfad explizit schützen müssen, um zu vermeiden, dass zur Laufzeit ungültige Anweisungs ausnahmen generiert werden. Stellen Sie sicher, dass die Codepfade erheblich genug Arbeit leisten, um die zusätzlichen Kosten für Verzweigungen, 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