Besonderheiten von Bibliotheken
In diesem Thema wird der interne Entwurf der DirectXMath-Bibliothek beschrieben.
- Aufrufkonventionen
- Äquivalenz der Grafikbibliothekstypen
- Globale Konstanten in der DirectXMath-Bibliothek
- Windows SSE im Vergleich zu SSE2
- Routinevarianten
- Plattforminkonsistenzen
- Plattformspezifische Erweiterungen
- Verwandte Themen
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. Wenn Sie XMVECTOR-Objekte als Parameter übergeben, die an einer Grenze von 16 Byte ausgerichtet 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 verbleibende Argumente über den Stapel übergibt.
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 XMMATRIX bezeichnet) ü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 eine effiziente Übergabe von __m128 Werten zur Verfügung. Der Standard ist __fastcall, der alle __m128 Werte im Stapel übergibt.
Neuere Visual Studio-Compiler unterstützen die 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 XMMATRIX bezeichnet) über SSE/SSE2-Register übergeben, wenn ausreichend Platz vorhanden ist.
Für Windows auf ARM
Windows auf ARM & ARM64 unterstützt das Übergeben der ersten vier __n128 Werte (XMVECTOR-Instanzen) im Register.
DirectXMath-Lösung
Die Aliases FXMVECTOR, GXMVECTOR, HXMVECTOR und CXMVECTOR unterstützen die folgenden Konventionen:
- Verwenden Sie den AliasFXMVECTOR, um an die ersten drei Instanzen von XMVECTOR zu übergeben, die als Argumente für eine Funktion verwendet werden.
- Verwenden Sie den AliasGXMVECTOR, um die 4. Instanz eines XMVECTOR zu übergeben, der als Argument an eine Funktion verwendet wird.
- Verwenden Sie den AliasHXMVECTOR, um die 5. und 6. Instanz eines XMVECTOR zu übergeben, der als Argument an eine Funktion verwendet wird. Weitere Informationen finden Sie in der __vectorcall Dokumentation.
- Verwenden Sie den Alias CXMVECTOR, um alle weiteren Instanzen von XMVECTOR als Argumente zu übergeben.
Hinweis
Verwenden Sie für Ausgabeparameter immer XMVECTOR* oder XMVECTOR& und ignorieren Sie sie in Bezug auf die vorherigen Regeln für Eingabeparameter.
Aufgrund von Einschränkungen bei __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 Aliases FXMMATRIX und CXMMATRIX unterstützen die Nutzung des HVA-Arguments, das mit __vectorcall übergeben wird.
- Verwenden Sie den Alias FXMMATRIX, um die erste XMMATRIX als Argument an die Funktion zu übergeben. Dies geht davon aus, 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 finden Sie in der __vectorcall Dokumentation.
- Verwenden Sie andernfalls den Alias CXMMATRIX .
Aufgrund von Einschränkungen bei __vectorcall empfehlen wir, FXMMATRIX für C++-Konstruktoren nie 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 für C++-Konstruktoren nicht zu verwenden.
Im Folgenden sind Beispieldeklarationen aufgeführt, 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 Typaliasen wie folgt definiert (Parameter müssen vom Wert übergeben werden, damit der Compiler diese für die In-Register-Übergabe in Betracht ziehen 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 64-Bit-native 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 werden 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 verwenden, und in diesen Fällen möchten wir die bestmögliche Aufrufkonvention für jede Plattform.
Äquivalenz der Grafikbibliothekstypen
Zur Unterstützung der Verwendung der DirectXMath-Bibliothek entsprechen viele DirectXMath-Bibliothekstypen und -strukturen den Windows-Implementierungen der D3DDECLTYPE- und D3DFORMAT-Typen 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 verringern, verwendet die DirectXMath-Bibliothek das XMGLOBALCONST-Makro, um eine Reihe globaler interner Konstanten in ihrer Implementierung zu verwenden. In der Konvention werden solche internen globalen Konstanten durch g_XM präfixiert. In der Regel sind sie einer der folgenden Typen: XMVECTORU32, XMVECTORF32 oder XMVECTORI32.
Diese internen globalen Konstanten können in zukünftigen Überarbeitungen der DirectXMath-Bibliothek geändert werden. Verwenden Sie öffentliche Funktionen, die die Konstanten kapseln, wenn möglich, anstatt direkt g_XM globalen Werte zu verwenden. Sie können auch eigene globale Konstanten mit XMGLOBALCONST deklarieren.
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 ganzzahlige Vektorunterstützung bereitzustellen. SSE2 wird von allen Intel-Prozessoren seit der Einführung des Pentium 4, aller AMD K8 und höher Prozessoren und aller 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, mit denen Sie Ihre Arbeit einfacher erledigen können:
- Vergleichsfunktionen zum Erstellen komplizierter bedingter Verzweigungen basierend auf einer kleineren Anzahl von Vektorvergleichsvorgängen. Der Name dieser Funktionen endet in „R” wie XMVector3InBoundsR. Die Funktionen geben einen Vergleichsdatensatz als UINT-Rückgabewert oder als UINT-Ausgabeparameter zurück. Sie können die XMComparision*-Makros verwenden, um den Wert zu testen.
- Batchfunktionen zum Ausführen von Batchformatvorgä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 sie generieren ein Array von Ausgaben. In der Regel nehmen sie einen Eingabe- und Ausgabeschritt.
- Schätzungsfunktionen, die eine schnellere Schätzung anstelle eines langsameren, genaueren Ergebnisses implementieren. Der Name dieser Funktionen endet in „Est”, z. B. XMVector3NormalizeEst. Die Qualität und Leistung der Nutzungsschätzung variieren von Plattform zu Plattform, es wird jedoch empfohlen, Schätzungsvarianten für leistungssensitiven Code zu verwenden.
Plattforminkonsistenzen
Die DirectXMath-Bibliothek ist für die Verwendung in leistungsempfindlichen Grafikanwendungen und -spielen vorgesehen. Daher ist die Implementierung für eine optimale Geschwindigkeit bei normaler Verarbeitung auf allen unterstützten Plattformen konzipiert. Die Ergebnisse bei Grenzbedingungen, 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 das SSE-Steuerelementwort für Windows 32-Bit und 64-Bit. Darüber hinaus gibt es Unterschiede bei den Grenzbedingungen zwischen verschiedenen CPU-Anbietern.
Verwenden Sie DirectXMath nicht in wissenschaftlichen oder anderen Anwendungen, bei denen die numerische Genauigkeit von größter Bedeutung ist. Diese Einschränkung spiegelt sich auch im Mangel an Unterstützung für doppelte oder andere erweiterte Genauigkeitsberechnungen wider.
Hinweis
Die _XM_NO_INTRINSICS_ skalaren Codepfade werden in der Regel für Compliance geschrieben, nicht für die Leistung. Ihre Grenzbedingungsergebnisse variieren ebenfalls.
Plattformspezifische Erweiterungen
Die DirectXMath-Bibliothek soll die C++-SIMD-Programmierung vereinfachen, die hervorragende Unterstützung für x86-, x64- und Windows RT-Plattformen mit allgemein unterstützten systeminternen Anweisungen (SSE2 und ARM-NEON) bietet.
Es gibt jedoch Fälle, in denen plattformspezifische Anweisungen nützlich sein können. Aufgrund der Art und Weise, wie DirectXMath implementiert ist, ist es in vielen Fällen trivial, DirectXMath-Typen direkt in Standard-Compiler-unterstützten Intrinsics-Anweisungen zu verwenden und DirectXMath als Fallback-Pfad für Plattformen zu nutzen, die die erweiterte Anweisung nicht unterstützen.
Hier ist beispielsweise ein vereinfachtes Beispiel für die Nutzung der SSE 4.1 Punktproduktanweisung. Beachten Sie, dass Sie den Codepfad explizit schützen müssen, um ungültige Anweisungsausnahmen zur Laufzeit zu vermeiden. Stellen Sie sicher, dass die Codepfade ausreichend Arbeit leisten, um die zusätzlichen Kosten für Verzweigungen, die Komplexität der Pflege 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 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