Sdílet prostřednictvím


Interní funkce knihovny

Toto téma popisuje interní návrh knihovny DirectXMath.

Konvence volání

Pokud chcete zlepšit přenositelnost a optimalizovat rozložení dat, musíte pro každou platformu podporovanou knihovnou DirectXMath použít odpovídající konvence volání. Konkrétně když předáte XMVECTOR objekty jako parametry, které jsou definovány jako zarovnané na hranici 16 bajtů, existují různé sady požadavků volání v závislosti na cílové platformě:

pro 32bitovou verzi Windows

Pro 32bitovou verzi Windows jsou k dispozici dvě konvence volání pro efektivní předávání hodnot __m128 (které implementuje XMVECTOR na této platformě). Standard je __fastcall, který může předat první tři __m128 hodnoty (XMVECTOR instance) jako argumenty funkce v SSE/SSE2 registru. __fastcall předává zbývající argumenty prostřednictvím zásobníku.

Novější kompilátory sady Microsoft Visual Studio podporují novou konvenci volání, __vectorcall, která může předat až šest hodnot __m128 (XMVECTOR instancí) jako argumenty funkce v SSE/SSE2 registru. Může také předávat heterogenní vektorové agregace (označované také jako XMMATRIX) prostřednictvím SSE/SSE2 registruje, pokud je dostatek místa.

Pro 64bitové edice windows

Pro 64bitovou verzi Windows jsou k dispozici dvě konvence volání pro efektivní předávání hodnot __m128. Standard je __fastcall, který předává všechny __m128 hodnoty v zásobníku.

Novější kompilátory sady Visual Studio podporují konvenci volání __vectorcall, která může předat až šest __m128 hodnot (XMVECTOR instancí) jako argumenty funkce v SSE/SSE2 registru. Může také předávat heterogenní vektorové agregace (označované také jako XMMATRIX) prostřednictvím SSE/SSE2 registruje, pokud je dostatek místa.

pro Windows na ARM

Windows v ARM & ARM64 podporuje předávání prvních čtyř __n128 hodnot (XMVECTOR instancí) v registru.

řešení DirectXMath

Aliasy FXMVECTOR, GXMVECTOR, HXMVECTORa CXMVECTOR podporují tyto konvence:

  • Pomocí aliasu FXMVECTOR předejte až první tři instance XMVECTOR jako argumenty funkce.
  • Pomocí aliasu GXMVECTOR předejte 4. instanci XMVECTOR, která se používá jako argument funkce.
  • Pomocí aliasu HXMVECTOR předejte 5. a 6. instanceXMVECTORXMVECTOR jako argument funkce. Další informace o dalších aspektech najdete v dokumentaci k __vectorcall.
  • Pomocí aliasu CXMVECTOR předejte všechny další instance XMVECTOR použité jako argumenty.

Poznámka

Pro výstupní parametry vždy použijte XMVECTOR* nebo XMVECTOR& a ignorujte je s ohledem na předchozí pravidla pro vstupní parametry.

 

Z důvodu omezení __vectorcall doporučujeme pro konstruktory C++ nepoužívat GXMVECTOR ani HXMVEC TOR. Stačí použít FXMVECTOR pro první tři hodnoty XMVECTOR a pak pro zbytek použít CXMVECTOR.

Aliasy FXMMATRIX a CXMMATRIX pomáhají využít výhod předávání argumentů HVA s __vectorcall.

  • Pomocí aliasu FXMMATRIX předejte první XMMATRIX jako argument funkci. Předpokládá se, že nemáte více než dva FXMVECTOR argumenty nebo více než dva float, double nebo FXMVECTOR argumenty napravo od matice. Další informace o dalších aspektech najdete v dokumentaci k __vectorcall.
  • Jinak použijte alias CXMMATRIX.

Z důvodu omezení __vectorcall doporučujeme nikdy používat FXMMATRIX pro konstruktory jazyka C++. Stačí použít CXMMATRIX.

Kromě aliasů typů musíte také použít XM_CALLCONV poznámku, abyste měli jistotu, že funkce používá odpovídající konvenci volání (__fastcall a __vectorcall) na základě kompilátoru a architektury. Z důvodu omezení __vectorcall doporučujeme nepoužívat XM_CALLCONV pro konstruktory jazyka C++.

Následuje příklad deklarací, které ilustrují tuto konvenci:

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

Aby bylo možné tyto konvence volání podporovat, jsou tyto aliasy typů definovány následujícím způsobem (parametry musí být předány hodnotou, aby je kompilátor zvážil pro předávání v registru):

pro 32bitové aplikace pro Windows

Při použití __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;

Při použití __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;

pro 64bitové nativní aplikace pro Windows

Při použití __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;

Při použití __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 v ARM

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

Poznámka

I když jsou všechny funkce deklarovány jako vložené a v mnoha případech kompilátor nebude muset pro tyto funkce používat konvence volání, existují případy, kdy se kompilátor může rozhodnout, že je efektivnější neinlineovat funkci, a v těchto případech chceme nejlepší možnou konvenci volání pro každou platformu.

 

Ekvivalence typů grafické knihovny

Aby bylo možné podporovat použití knihovny DirectXMath, mnoho typů a struktur knihovny DirectXMath jsou ekvivalentní implementacím D3DDECLTYPE a D3DFORMAT typů, stejně jako typy DXGI_FORMAT.

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

 

Globální konstanty v knihovně DirectXMath

Ke zmenšení velikosti datového segmentu používá knihovna DirectXMath XMGLOBALCONST makro k použití řady globálních interních konstant v jeho implementaci. Podle konvence jsou takové interní globální konstanty předponou g_XM. Obvykle se jedná o jeden z následujících typů: XMVECTORU32, XMVECTORF32nebo XMVECTORI32.

Tyto interní globální konstanty se můžou v budoucích revizích knihovny DirectXMath změnit. Používejte veřejné funkce, které zapouzdřují konstanty, pokud je to možné, místo přímého použití g_XM globálních hodnot. Můžete také deklarovat vlastní globální konstanty pomocí XMGLOBALCONST.

Windows SSE versus SSE2

Instrukční sada SSE poskytuje podporu pouze pro vektory s plovoucí desetinnou čárkou s jednoduchou přesností. DirectXMath musí použít instrukční sadu SSE2 k zajištění celočíselné podpory. SSE2 podporuje všechny procesory Intel od zavedení Procesoru Pentium 4, všech procesorů AMD K8 a novějších a všech procesorů s podporou x64.

Poznámka

Windows 8 pro x86 nebo novější vyžaduje podporu pro SSE2. Všechny verze Windows x64 vyžadují podporu pro SSE2. Windows v ARM / ARM64 vyžaduje ARM_NEON.

 

Rutinní varianty

Existuje několik variant funkcí DirectXMath, které usnadňují práci:

  • Funkce porovnání, které vytvářejí složité podmíněné větvení na základě menšího počtu operací porovnání vektorů. Název těchto funkcí končí na "R", například XMVector3InBoundsR. Funkce vrátí srovnávací záznam jako návratovou hodnotu UINT nebo jako parametr UINT out. K otestování hodnoty můžete použít XMComparision* makra.
  • Dávkové funkce pro provádění dávkových operací u větších vektorových polí Název těchto funkcí končí na "Stream", například XMVector3TransformStream. Funkce pracují s polem vstupů a generují pole výstupů. Obvykle přebírají vstupní a výstupní krok.
  • Funkce odhadu, které implementují rychlejší odhad místo pomalejšího a přesnějšího výsledku. Název těchto funkcí končí na "Est", například XMVector3NormalizeEst. Vliv na kvalitu a výkon při použití odhadu se liší od platformy po platformu, ale doporučujeme použít pro kód citlivý na výkon odhad varianty.

Nekonzistence platforem

Knihovna DirectXMath je určená pro použití v grafických aplikacích a hrách citlivých na výkon. Proto je implementace navržena pro optimální rychlost normálního zpracování na všech podporovaných platformách. Výsledky v podmínkách hranic, zejména těch, které generují speciální desetiny s plovoucí desetinou čárkou, se pravděpodobně budou lišit od cíle k cíli. Toto chování bude také záviset na jiných nastaveních za běhu, jako je například slovo ovládacího prvku x87 pro cíl bez vnitřních objektů windows nebo slovo SSE pro windows 32bitovou i 64bitovou verzi. Kromě toho budou mezi různými dodavateli procesoru rozdíly v hraničních podmínkách.

Nepoužívejte DirectXMath ve vědeckých nebo jiných aplikacích, kde je numerická přesnost nejdůležitější. Toto omezení se také odráží v nedostatku podpory pro dvojité nebo jiné výpočty s rozšířenou přesností.

Poznámka

Cesty _XM_NO_INTRINSICS_ skalárního kódu jsou obecně napsané pro dodržování předpisů, nikoli pro výkon. Výsledky jejich hraniční podmínky se také budou lišit.

 

Rozšíření specifická pro platformu

Knihovna DirectXMath je určená ke zjednodušení programování C++ SIMD a poskytuje vynikající podporu pro platformy x86, x64 a Windows RT pomocí široce podporovaných vnitřních instrukcí (SSE2 a ARM-NEON).

Existují však časy, kdy mohou být pokyny specifické pro platformu přínosné. Vzhledem ke způsobu implementace DirectXMath je v mnoha případech triviální použít typy DirectXMath přímo ve standardních příkazech vnitřních objektů podporovaných kompilátorem a použít DirectXMath jako záložní cestu pro platformy, které nepodporují rozšířenou instrukci.

Tady je například zjednodušený příklad využití instrukce SSE 4.1 dot-product. Nezapomeňte, že musíte explicitně chránit cestu kódu, abyste se vyhnuli generování neplatných výjimek instrukce za běhu. Zajistěte, aby cesty kódu fungovaly dostatečně dlouho, aby ospravedlňovaly dodatečné náklady na větvení, složitost údržby více cest kódu atd.

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

Další informace o rozšířeních specifických pro platformu najdete tady:

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

Průvodce programováním DirectXMath