Bagikan melalui


Internal Pustaka

Topik ini menjelaskan desain internal pustaka DirectXMath.

Konvensi Panggilan

Untuk meningkatkan portabilitas dan mengoptimalkan tata letak data, Anda perlu menggunakan konvensi panggilan yang sesuai untuk setiap platform yang didukung oleh Pustaka DirectXMath. Secara khusus, ketika Anda meneruskan objek XMVECTOR sebagai parameter, yang didefinisikan sebagai selaras pada batas 16 byte, ada serangkaian persyaratan panggilan yang berbeda, tergantung pada platform target:

Untuk Windows 32-bit

Untuk Windows 32-bit, ada dua konvensi panggilan yang tersedia untuk meneruskan nilai __m128 yang efisien (yang mengimplementasikan XMVECTOR pada platform tersebut). Standarnya adalah __fastcall, yang dapat meneruskan tiga nilai __m128 pertama (instans XMVECTOR ) sebagai argumen ke fungsi dalam register SSE/SSE2 . __fastcall meneruskan argumen yang tersisa melalui tumpukan.

Pengkompilasi Microsoft Visual Studio yang lebih baru mendukung konvensi panggilan baru, __vectorcall, yang dapat meneruskan hingga enam nilai __m128 (instans XMVECTOR ) sebagai argumen ke fungsi dalam register SSE/SSE2 . Ini juga dapat melewati agregat vektor heterogen (juga dikenal sebagai XMMATRIX) melalui SSE/SSE2 mendaftar jika ada ruang yang cukup.

Untuk Windows edisi 64-bit

Untuk Windows 64-bit, ada dua konvensi panggilan yang tersedia untuk melewati nilai __m128 yang efisien. Standarnya adalah __fastcall, yang melewati semua nilai __m128 pada tumpukan.

Pengkompilasi Visual Studio yang lebih baru mendukung konvensi panggilan __vectorcall, yang dapat meneruskan hingga enam nilai __m128 (instans XMVECTOR ) sebagai argumen ke fungsi dalam register SSE/SSE2 . Ini juga dapat melewati agregat vektor heterogen (juga dikenal sebagai XMMATRIX) melalui SSE/SSE2 mendaftar jika ada ruang yang cukup.

Untuk Windows di ARM

Windows di ARM & ARM64 mendukung passing empat nilai __n128 pertama (instans XMVECTOR ) dalam daftar.

Solusi DirectXMath

Alias FXMVECTOR, GXMVECTOR, HXMVECTOR, dan CXMVECTOR mendukung konvensi ini:

  • Gunakan alias FXMVECTOR untuk meneruskan ke tiga instans pertama XMVECTOR yang digunakan sebagai argumen ke fungsi.
  • Gunakan alias GXMVECTOR untuk meneruskan instans ke-4 dari XMVECTOR yang digunakan sebagai argumen ke fungsi.
  • Gunakan alias HXMVECTOR untuk meneruskan instans ke-5 dan ke-6 dari XMVECTOR yang digunakan sebagai argumen ke fungsi. Untuk informasi tentang pertimbangan tambahan, lihat dokumentasi __vectorcall.
  • Gunakan alias CXMVECTOR untuk meneruskan instans XMVECTOR lebih lanjut yang digunakan sebagai argumen.

Catatan

Untuk parameter output, selalu gunakan XMVECTOR* atau XMVECTOR& dan abaikan sehubungan dengan aturan sebelumnya untuk parameter input.

 

Karena keterbatasan dengan __vectorcall, kami sarankan Anda tidak menggunakan GXMVECTOR atau HXMVECTOR untuk konstruktor C++. Cukup gunakan FXMVECTOR untuk tiga nilai XMVECTOR pertama, lalu gunakan CXMVECTOR untuk sisanya.

Alias FXMMATRIX dan CXMMATRIX membantu mendukung memanfaatkan argumen HVA yang diteruskan dengan __vectorcall.

  • Gunakan alias FXMMATRIX untuk meneruskan XMMATRIX pertama sebagai argumen ke fungsi . Ini mengasumsikan Anda tidak memiliki lebih dari dua argumen FXMVECTOR atau lebih dari dua argumen float, double, atau FXMVECTOR ke 'kanan' matriks. Untuk informasi tentang pertimbangan tambahan, lihat dokumentasi __vectorcall.
  • Gunakan alias CXMMATRIX jika tidak.

Karena keterbatasan dengan __vectorcall, kami sarankan Anda tidak pernah menggunakan FXMMATRIX untuk konstruktor C++. Cukup gunakan CXMMATRIX.

Selain alias jenis, Anda juga harus menggunakan anotasi XM_CALLCONV untuk memastikan fungsi menggunakan konvensi panggilan yang sesuai (__fastcall versus __vectorcall) berdasarkan kompilator dan arsitektur Anda. Karena keterbatasan dengan __vectorcall, kami sarankan Anda tidak menggunakan XM_CALLCONV untuk konstruktor C++.

Berikut ini adalah contoh deklarasi yang mengilustrasikan konvensi ini:

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

Untuk mendukung konvensi panggilan ini, alias jenis ini didefinisikan sebagai berikut (parameter harus diteruskan oleh nilai bagi pengkompilasi untuk mempertimbangkannya untuk passing in-register):

Untuk aplikasi Windows 32-bit

Saat Anda menggunakan __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;

Saat Anda menggunakan __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;

Untuk aplikasi Windows asli 64-bit

Saat Anda menggunakan __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;

Saat Anda menggunakan __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 di ARM

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

Catatan

Meskipun semua fungsi dinyatakan sebaris dan dalam banyak kasus pengkompilasi tidak perlu menggunakan konvensi panggilan untuk fungsi-fungsi ini, ada kasus di mana pengkompilasi dapat memutuskan lebih efisien untuk tidak sebaris fungsi dan dalam kasus ini kami menginginkan konvensi panggilan terbaik untuk setiap platform.

 

Kesetaraan Tipe Pustaka Grafis

Untuk mendukung penggunaan Pustaka DirectXMath, banyak jenis dan struktur Pustaka DirectXMath setara dengan implementasi Windows dari jenis D3DDECLTYPE dan D3DFORMAT , serta jenis DXGI_FORMAT .

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

 

Konstanta Global di Pustaka DirectXMath

Untuk mengurangi ukuran segmen data, Pustaka DirectXMath menggunakan makro XMGLOBALCONST untuk menggunakan sejumlah konstanta internal global dalam implementasinya. Menurut konvensi, konstanta global internal tersebut diawali oleh g_XM. Biasanya, mereka adalah salah satu jenis berikut: XMVECTORU32, XMVECTORF32, atau XMVECTORI32.

Konstanta global internal ini dapat berubah dalam revisi Pustaka DirectXMath di masa mendatang. Gunakan fungsi publik yang merangkum konstanta jika memungkinkan daripada penggunaan langsung g_XM nilai global. Anda juga dapat mendeklarasikan konstanta global Anda sendiri menggunakan XMGLOBALCONST.

Windows SSE versus SSE2

Set instruksi SSE menyediakan dukungan hanya untuk vektor floating-point presisi tunggal. DirectXMath harus menggunakan set instruksi SSE2 untuk memberikan dukungan vektor bilangan bulat. SSE2 didukung oleh semua prosesor Intel sejak pengenalan Pentium 4, semua prosesor AMD K8 dan yang lebih baru, dan semua prosesor berkemampuan x64.

Catatan

Windows 8 untuk x86 atau yang lebih baru memerlukan dukungan untuk SSE2. Semua versi Windows x64 memerlukan dukungan untuk SSE2. Windows pada ARM / ARM64 memerlukan ARM_NEON.

 

Varian Rutin

Ada beberapa varian fungsi DirectXMath yang membuatnya lebih mudah untuk melakukan pekerjaan Anda:

  • Fungsi perbandingan untuk membuat percabangan kondisional yang rumit berdasarkan sejumlah kecil operasi perbandingan vektor. Nama fungsi-fungsi ini berakhiran "R" seperti XMVector3InBoundsR. Fungsi mengembalikan rekaman perbandingan sebagai nilai pengembalian UINT, atau sebagai parameter keluar UINT. Anda dapat menggunakan makro XMComparision* untuk menguji nilainya.
  • Fungsi batch untuk melakukan operasi gaya batch pada array vektor yang lebih besar. Nama fungsi-fungsi ini berakhiran "Stream" seperti XMVector3TransformStream. Fungsi ini beroperasi pada array input, dan menghasilkan array output. Biasanya, mereka mengambil langkah input dan output.
  • Fungsi estimasi yang mengimplementasikan estimasi yang lebih cepat alih-alih hasil yang lebih lambat dan lebih akurat. Nama fungsi-fungsi ini berakhiran "Est" seperti XMVector3NormalizeEst. Dampak kualitas dan performa penggunaan estimasi bervariasi dari platform ke platform, tetapi kami sarankan Anda menggunakan varian estimasi untuk kode sensitif performa.

Inkonsistensi Platform

Pustaka DirectXMath ditujukan untuk digunakan dalam aplikasi dan game grafis yang sensitif terhadap performa. Oleh karena itu, implementasi dirancang untuk kecepatan optimal melakukan pemrosesan normal pada semua platform yang didukung. Hasil pada kondisi batas, terutama yang menghasilkan khusus floating-point, cenderung bervariasi dari target ke target. Perilaku ini juga akan bergantung pada pengaturan run-time lainnya, seperti kata kontrol x87 untuk target no-intrinsik Windows 32-bit atau kata kontrol SSE untuk Windows 32-bit dan 64-bit. Selain itu, akan ada perbedaan kondisi batas antara berbagai vendor CPU.

Jangan gunakan DirectXMath dalam aplikasi ilmiah atau aplikasi lain di mana akurasi numerik sangat penting. Selain itu, batasan ini tercermin dalam kurangnya dukungan untuk komputasi presisi ganda atau lainnya yang diperpanjang.

Catatan

Jalur kode skalar _XM_NO_INTRINSICS_ umumnya ditulis untuk kepatuhan, bukan performa. Hasil kondisi batas mereka juga akan bervariasi.

 

Ekstensi khusus platform

Pustaka DirectXMath dimaksudkan untuk menyederhanakan pemrograman C++ SIMD yang memberikan dukungan yang sangat baik untuk platform x86, x64, dan Windows RT menggunakan instruksi intrinsik yang didukung secara luas (SSE2 dan ARM-NEON).

Namun, ada kalanya instruksi khusus platform dapat terbukti bermanfaat. Karena cara DirectXMath diterapkan, dalam banyak kasus sepele untuk menggunakan jenis DirectXMath langsung dalam pernyataan intrinsik standar yang didukung kompilator, dan untuk menggunakan DirectXMath sebagai jalur fallback untuk platform yang tidak mendukung instruksi yang diperluas.

Misalnya, berikut adalah contoh yang disederhanakan untuk memanfaatkan instruksi SSE 4.1 dot-product. Perhatikan bahwa Anda harus secara eksplisit menjaga jalur kode untuk menghindari pembuatan pengecualian instruksi yang tidak valid pada waktu proses. Pastikan jalur kode melakukan pekerjaan yang cukup signifikan untuk membenarkan biaya tambahan percabangan, kompleksitas mempertahankan beberapa jalur kode, dan sebagainya.

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

Untuk informasi selengkapnya tentang ekstensi khusus platform, lihat:

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

Panduan Pemrograman DirectXMath