라이브러리 내부 구조
이 항목에서는 DirectXMath 라이브러리의 내부 디자인에 대해 설명합니다.
호출 규칙
이식성을 향상시키고 데이터 레이아웃을 최적화하려면 DirectXMath 라이브러리에서 지원하는 각 플랫폼에 적절한 호출 규칙을 사용해야 합니다. 특히 XMVECTOR 개체를 16비트 경계에 맞춰 정의된 매개 변수로 전달하는 경우 대상 플랫폼에 따라 다른 호출 요구 사항 집합이 있습니다.
32비트 Windows의 경우
32비트 Windows의 경우 __m128 값을 효율적으로 전달하는 데 사용할 수 있는 두 가지 호출 규칙이 있습니다(해당 플랫폼에서 XMVECTOR를 구현). 표준은 처음 세 개의 __m128 값(XMVECTOR 인스턴스)을 SSE/SSE2 레지스터의 함수에 인수로 전달할 수 있는 __fastcall. __fastcall 스택을 통해 나머지 인수를 전달합니다.
최신 Microsoft Visual Studio 컴파일러는 SSE/SSE2 레지스터의 함수에 인수로 최대 6개의 __m128 값(XMVECTOR 인스턴스)을 전달할 수 있는 새 호출 규칙 __vectorcall 지원합니다. 충분한 공간이 있는 경우 SSE/SSE2 레지스터를 통해 다른 유형의 벡터 집계(XMMATRIX라고도 함)를 전달할 수도 있습니다.
Windows 64비트 버전의 경우
64비트 Windows의 경우 __m128 값을 효율적으로 전달하는 데 사용할 수 있는 두 가지 호출 규칙이 있습니다. 표준은 스택의 모든 __m128 값을 전달하는 __fastcall.
최신 Visual Studio 컴파일러는 SSE/SSE2 레지스터의 함수에 인수로 최대 6개의 __m128 값(XMVECTOR 인스턴스)을 전달할 수 있는 __vectorcall 호출 규칙을 지원합니다. 충분한 공간이 있는 경우 SSE/SSE2 레지스터를 통해 다른 유형의 벡터 집계(XMMATRIX라고도 함)를 전달할 수도 있습니다.
ARM의 Windows용
ARM 및 ARM64의 Windows는 처음 4개의 __n128 값(XMVECTOR 인스턴스) 인 레지스터 전달을 지원합니다.
DirectXMath 솔루션
FXMVECTOR, GXMVECTOR, HXMVECTOR 및 CXMVECTOR 별칭은 다음 규칙을 지원합니다.
- FXMVECTOR 별칭을 사용하여 함수에 인수로 사용되는 XMVECTOR의 처음 세 인스턴스까지 전달합니다.
- GXMVECTOR 별칭을 사용하여 인수로 사용되는 XMVECTOR의 4번째 인스턴스를 함수에 전달합니다.
- HXMVECTOR 별칭을 사용하여 함수에 인수로 사용되는 XMVECTOR의 5번째 및 6번째 인스턴스를 전달합니다. 추가 고려 사항에 대한 자세한 내용은 __vectorcall 설명서를 참조하세요.
- CXMVECTOR 별칭을 사용하여 인수로 사용되는 XMVECTOR의 추가 인스턴스를 전달합니다.
참고 항목
출력 매개 변수의 경우 항상 XMVECTOR* 또는 XMVECTOR>를 사용하고 입력 매개 변수에 대한 이전 규칙과 관련하여 무시합니다.
__vectorcall 제한 사항 때문에 C++ 생성자에 GXMVECTOR 또는 HXMVECTOR를 사용하지 않는 것이 좋습니다. 처음 세 개의 XMVECTOR 값에 FXMVECTOR를 사용한 다음 나머지 값에 CXMVECTOR를 사용합니다.
FXMMATRIX 및 CXMMATRIX 별칭은 __vectorcall 전달되는 HVA 인수를 활용할 수 있도록 지원합니다.
- FXMMATRIX 별칭을 사용하여 첫 번째 XMMATRIX를 함수에 인수로 전달합니다. 이 경우 행렬의 '오른쪽'에 FXMVECTOR 인수가 두 개 이상 없거나 부동 소수점 인수, double 또는 FXMVECTOR 인수가 두 개 이상 없다고 가정합니다. 추가 고려 사항에 대한 자세한 내용은 __vectorcall 설명서를 참조하세요.
- 그렇지 않으면 CXMMATRIX 별칭을 사용합니다.
__vectorcall 제한 사항 때문에 C++ 생성자에 FXMMATRIX를 사용하지 않는 것이 좋습니다. CXMMATRIX만 사용합니다.
형식 별칭 외에도 XM_CALLCONV 주석을 사용하여 함수가 컴파일러 및 아키텍처에 따라 적절한 호출 규칙(__fastcall 및 __vectorcall)을 사용하는지 확인해야 합니다. __vectorcall 제한 사항 때문에 C++ 생성자에 XM_CALLCONV 사용하지 않는 것이 좋습니다.
다음은 이 규칙을 보여 주는 예제 선언입니다.
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);
이러한 호출 규칙을 지원하기 위해 이러한 형식 별칭은 다음과 같이 정의됩니다(매개 변수는 등록 내 전달을 고려하려면 컴파일러의 값으로 전달되어야 합니다.)
32비트 Windows 앱의 경우
__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;
__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;
64비트 네이티브 Windows 앱의 경우
__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;
__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;
ARM 기반 Windows
typedef const XMVECTOR FXMVECTOR;
typedef const XMVECTOR GXMVECTOR;
typedef const XMVECTOR& CXMVECTOR;
typedef const XMMATRIX& FXMMATRIX;
typedef const XMMATRIX& CXMMATRIX;
참고 항목
모든 함수가 인라인으로 선언되고 대부분의 경우 컴파일러가 이러한 함수에 대해 호출 규칙을 사용할 필요가 없지만, 컴파일러가 함수를 인라인하지 않는 것이 더 효율적이라고 판단할 수 있는 경우가 있으며, 이러한 경우 각 플랫폼에 대해 최상의 호출 규칙을 원합니다.
그래픽 라이브러리 형식 동등성
DirectXMath 라이브러리의 사용을 지원하기 위해 많은 DirectXMath 라이브러리 형식 및 구조는 D3DDECLTYPE 및 D3DFORMAT 형식의 Windows 구현뿐만 아니라 DXGI_FORMAT 형식과 동일합니다.
DirectXMath | D3DDECLTYPE | D3DFORMAT | DXGI_FORMAT |
---|---|---|---|
XMBYTE2 | DXGI_FORMAT_R8G8_SINT | ||
XMBYTE4 | D3DDECLTYPE_BYTE4(Xbox 전용) | D3DFMT_x8x8x8x8 | DXGI_FORMAT_x8x8x8x8_SINT |
XMBYTEN2 | D3DFMT_V8U8 | DXGI_FORMAT_R8G8_SNORM | |
XMBYTEN4 | D3DDECLTYPE_BYTE4N(Xbox만 해당) | D3DFMT_x8x8x8x8 | DXGI_FORMAT_x8x8x8x8_SNORM |
XMCOLOR | D3DDECLTYPE_D3DCOLOR | D3DFMT_A8R8G8B8 | DXGI_FORMAT_B8G8R8A8_UNORM(DXGI 1.1 이상) |
XMDEC4 | D3DDECLTYPE_DEC4(Xbox만 해당) | D3DDECLTYPE_DEC3(Xbox만 해당) | |
XMDECN4 | D3DDECLTYPE_DEC4N(Xbox만 해당) | D3DDECLTYPE_DEC3N(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 및 XMStoreUDecN4_XR 사용) |
XMUDEC4 | D3DDECLTYPE_UDEC4(Xbox 전용) D3DDECLTYPE_UDEC3(Xbox만 해당) |
D3DFMT_A2R10G10B10 D3DFMT_A2B10G10R10 |
DXGI_FORMAT_R10G10B10A2_UINT |
XMUDECN4 | D3DDECLTYPE_UDEC4N(Xbox만 해당) D3DDECLTYPE_UDEC3N(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(Xbox 전용) | D3DFMT_x16x16x16x16 | DXGI_FORMAT_R16G16B16A16_UINT |
XMUSHORTN4 | D3DDECLTYPE_USHORT4N | D3DFMT_x16x16x16x16 | DXGI_FORMAT_R16G16B16A16_UNORM |
DirectXMath 라이브러리의 전역 상수
데이터 세그먼트의 크기를 줄이기 위해 DirectXMath 라이브러리는 XMGLOBALCONST 매크로를 사용하여 구현에서 여러 전역 내부 상수를 사용합니다. 규칙에 따라 이러한 내부 전역 상수는 g_XM 접두사로 지정됩니다. 일반적으로 XMVECTORU32, XMVECTORF32 또는 XMVECTORI32 유형 중 하나입니다.
이러한 내부 전역 상수는 DirectXMath 라이브러리의 향후 수정 버전에서 변경될 수 있습니다. g_XM 전역 값을 직접 사용하는 대신 가능하면 상수를 캡슐화하는 public 함수를 사용합니다. XMGLOBALCONST를 사용하여 고유한 전역 상수도 선언할 수 있습니다.
Windows SSE 및 SSE2
SSE 명령 집합은 단정밀도 부동 소수점 벡터에 대해서만 지원을 제공합니다. DirectXMath는 정수 벡터 지원을 제공하기 위해 SSE2 명령 집합을 사용해야 합니다. SSE2는 펜티엄 4, 모든 AMD K8 이상 프로세서 및 모든 x64 지원 프로세서가 도입된 이후 모든 Intel 프로세서에서 지원됩니다.
참고 항목
x86 이상용 Windows 8에는 SSE2에 대한 지원이 필요합니다. 모든 버전의 Windows x64에는 SSE2에 대한 지원이 필요합니다. ARM/ARM64의 Windows에는 ARM_NEON 필요합니다.
일상적인 변형
작업을 더 쉽게 수행할 수 있도록 하는 DirectXMath 함수에는 몇 가지 변형이 있습니다.
- 더 적은 수의 벡터 비교 작업을 기반으로 복잡한 조건부 분기를 만드는 비교 함수입니다. 이러한 함수의 이름은 XMVector3InBoundsR과 같은 "R"로 끝납니다. 함수는 비교 레코드를 UINT 반환 값 또는 UINT out 매개 변수로 반환합니다. XMComparision* 매크로를 사용하여 값을 테스트할 수 있습니다.
- 더 큰 벡터 배열에서 일괄 처리 스타일 작업을 수행하기 위한 Batch 함수입니다. 이러한 함수의 이름은 XMVector3TransformStream과 같은 "Stream"으로 끝납니다. 함수는 입력 배열에서 작동하며 출력 배열을 생성합니다. 일반적으로 입력 및 출력 보폭을 사용합니다.
- 더 느리고 정확한 결과 대신 더 빠른 추정을 구현하는 예측 함수입니다. 이러한 함수의 이름은 XMVector3NormalizeEst와 같은 "Est"로 끝납니다. 예측 사용의 품질 및 성능 영향은 플랫폼마다 다르지만 성능에 민감한 코드에는 예측 변형을 사용하는 것이 좋습니다.
플랫폼 불일치
DirectXMath 라이브러리는 성능에 민감한 그래픽 애플리케이션 및 게임에서 사용하기 위한 것입니다. 따라서 구현은 지원되는 모든 플랫폼에서 정상적인 처리를 수행하는 최적의 속도를 위해 설계되었습니다. 경계 조건, 특히 부동 소수점 특수를 생성하는 결과는 대상마다 다를 수 있습니다. 이 동작은 Windows 32비트 내장 함수 대상의 x87 컨트롤 단어 또는 Windows 32비트 및 64비트 모두에 대한 SSE 컨트롤 단어와 같은 다른 런타임 설정에도 따라 달라집니다. 또한 다양한 CPU 공급업체 간에 경계 조건에 차이가 있습니다.
숫자 정확도가 가장 중요한 과학 또는 기타 애플리케이션에서는 DirectXMath를 사용하지 마세요. 또한 이러한 제한은 이중 또는 기타 확장된 전체 자릿수 계산에 대한 지원이 부족하다는 것을 반영합니다.
참고 항목
_XM_NO_INTRINSICS_ 스칼라 코드 경로는 일반적으로 성능이 아닌 규정 준수를 위해 작성됩니다. 경계 조건 결과도 달라집니다.
플랫폼별 확장
DirectXMath 라이브러리는 광범위하게 지원되는 내장 함수 지침(SSE2 및 ARM-NEON)을 사용하여 x86, x64 및 Windows RT 플랫폼에 대한 우수한 지원을 제공하는 C++ SIMD 프로그래밍을 간소화하기 위한 것입니다.
그러나 플랫폼별 지침이 도움이 될 수 있는 경우가 있습니다. DirectXMath가 구현되는 방식 때문에 대부분의 경우 표준 컴파일러 지원 내장 함수 문에서 직접 DirectXMath 형식을 사용하고 확장 명령을 지원하지 않는 플랫폼에 대한 대체 경로로 DirectXMath를 사용하는 것은 간단합니다.
예를 들어 다음은 SSE 4.1 점 제품 명령을 활용하는 간소화된 예제입니다. 런타임에 잘못된 명령 예외를 생성하지 않도록 코드 경로를 명시적으로 보호해야 합니다. 코드 경로가 분기의 추가 비용, 여러 코드 경로 유지 관리의 복잡성 등을 정당화하기에 충분한 작업을 수행하도록 합니다.
#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;
}
플랫폼별 확장에 대한 자세한 내용은 다음을 참조하세요.
DirectXMath: SSE, SSE2 및 ARM-NEON
DirectXMath: SSE3 및 SSSE3
DirectXMath: SSE4.1 및 SSE4.2
DirectXMath: AVX
DirectXMath: F16C 및 FMA
DirectXMath: AVX2
DirectXMath: ARM64