Interní funkce knihovny
Toto téma popisuje interní návrh knihovny DirectXMath.
- konvence volání
- ekvivalence typů grafických knihoven
- globální konstanty v knihovně DirectXMath
- Windows SSE a SSE2
- rutinní varianty
- nekonzistence platformy
- rozšíření specifická pro platformu
- související témata
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
Související témata
-
Průvodce programováním DirectXMath