Games for Windows and the DirectX SDK blog

Technical tips, tricks, and news about game development for Microsoft platforms including desktop, Xbox, and UWP


Project maintained by walbourn Hosted on GitHub Pages — Theme by mattgraham
Home | Posts by Tag | Posts by Month

DirectXMath - SSE4.1 and SSE4.2

xbox, directxmath

Originally posted to Chuck Walbourn's Blog on MSDN,

The SSE4 instruction set consists of two parts, referred as SSE4.1 and 4.2. The intrinsics are located in the smmintrin.h header. The SSE4.1 instruction set is the most interesting for DirectXMath, while SSE 4.2 adds some more specialized instructions for CRC checks and string handling. The key new features are a flexible dot-product instruction, float4 vector rounding, a 2-vector ‘mux’ blend, and some specialized extract/insert operations.

A number of DirectXMath functions can be replaced with a single intrinsic when using SSE4.1.

XMVector2Dot(V1,V2) _mm_dp_ps( V1, V2, 0x3f )
XMVector3Dot(V1,V2) _mm_dp_ps( V1, V2, 0x7f )
XMVector4Dot(V1,V2) _mm_dp_ps( V1, V2, 0xff )
XMVectorRound(V) _mm_round_ps( V, _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC )
XMVectorTruncate(V) _mm_round_ps( V, _MM_FROUND_TO_ZERO | _MM_FROUND_NO_EXC )
XMVectorFloor(V) _mm_floor_ps( V )
XMVectorCeiling(V) _mm_ceil_ps( V )

The bit insert/extract instructions provide some specific optimization cases for vector accessors and setters. Here are the “Y” element versions, which can be extrapolated to the “Z” and “W” element versions very easily. Note that the standard scalar SSE/SSE2 mov already provides efficient support for the “X” element.

inline void XMVectorGetYPtr(float *y, FXMVECTOR V)
{
    *((int*)y) = _mm_extract_ps(V, 1);
}

inline uint32_t XMVectorGetIntY(FXMVECTOR V)
{
    __m128i V1 = _mm_castps_si128(V);
    return static_cast<uint32_t>(_mm_extract_epi32(V1, 1));
}

inline void XMVectorGetIntYPtr(uint32_t *y, FXMVECTOR V)
{
    __m128i V1 = _mm_castps_si128(V);
    *y = static_cast<uint32_t>(_mm_extract_epi32(V1, 1));
}

inline XMVECTOR XMVectorSetY(FXMVECTOR V, float y)
{
    XMVECTOR vResult = _mm_set_ss(y);
    vResult = _mm_insert_ps(V, vResult, 0x10);
    return vResult;
}

inline XMVECTOR XMVectorSetIntY(FXMVECTOR V, uint32_t y)
{
    __m128i vResult = _mm_castps_si128(V);
    vResult =
        _mm_insert_epi32(vResult, static_cast<int>(y), 1);
    return _mm_castsi128_ps(vResult);
}

The _mm_blend_ps instruction can be used as special-cases for the XMVectorPermute<> template. We’ll make more use of these in a future installment.

XMVectorPermute<4,1,2,3>(V1,V2) _mm_blend_ps(V1,V2,0x1)
XMVectorPermute<0,5,2,3>(V1,V2) _mm_blend_ps(V1,V2,0x2)
XMVectorPermute<4,5,2,3>(V1,V2) _mm_blend_ps(V1,V2,0x3)
XMVectorPermute<0,1,6,3>(V1,V2) _mm_blend_ps(V1,V2,0x4)
XMVectorPermute<4,1,6,3>(V1,V2) _mm_blend_ps(V1,V2,0x5)
XMVectorPermute<0,5,6,3>(V1,V2) _mm_blend_ps(V1,V2,0x6)
XMVectorPermute<4,5,6,3>(V1,V2) _mm_blend_ps(V1,V2,0x7)
XMVectorPermute<0,1,2,7>(V1,V2) _mm_blend_ps(V1,V2,0x8)
XMVectorPermute<4,1,2,7>(V1,V2) _mm_blend_ps(V1,V2,0x9)
XMVectorPermute<0,5,2,7>(V1,V2) _mm_blend_ps(V1,V2,0xA)
XMVectorPermute<4,5,2,7>(V1,V2) _mm_blend_ps(V1,V2,0xB)
XMVectorPermute<0,1,6,7>(V1,V2) _mm_blend_ps(V1,V2,0xC)
XMVectorPermute<4,1,6,7>(V1,V2) _mm_blend_ps(V1,V2,0xD)
XMVectorPermute<0,5,6,7>(V1,V2) _mm_blend_ps(V1,V2,0xE)

Processor Support

SSE4.1 is supported on Intel Core 2 (“Penryn”), Intel Core i7 (“Nehalem”), AMD Bulldozer, and later processors.

SSE 4.1 and SSE4.2 are supported on Intel Core i7 (“Nehalem”), AMD Bulldozer, and later processors. Intel Atom “Silvermont” or later support SSE 4.1 and SSE 4.2 as well.

#if defined(__clang__) || defined(__GNUC__)
#include <cpuid.h>
#else
#include <intrin.h>
#endif

int CPUInfo[4] = { -1 };
#if defined(__clang__) || defined(__GNUC__)
__cpuid(0, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]);
#else
__cpuid(CPUInfo, 0);
#endif
bool bSSE4_1 = false;
bool bSSE4_2 = false;
if (CPUInfo[0] > 0)
{
#if defined(__clang__) || defined(__GNUC__)
    __cpuid(1, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]);
#else
    __cpuid(CPUInfo, 1);
#endif
    bSSE4_1 = (CPUInfo[2] & 0x80000) != 0;
    bSSE4_2 = (CPUInfo[2] & 0x100000) != 0;
}

The Surface X Pro ARM64 device supports x86 with SSE, SSE2, SSE3, SSSE3, and SSE4.1 support.

Compiler Support

Support for SSE4.1 and SSE4.2 intrinsics was added to Visual Studio 2010.

Utility Code

Update: The source for this project is now available on GitHub under the MIT license.

Xbox: Xbox One and Xbox Series X|S support SSE4a, SSE4.1, and SSE4.2.

Update: Per the latest numbers from the Value Hardware Survey, for PC games you can require SSE4.1 / SSE4.2 support without excluding significant numbers of gamers. You should check for the CPU support at startup to avoid unexplained crashes due to invalid instructions if a customer tries to run it on an older PC.

See also: SSE, SSE2, and ARM-NEON; SSE3 and SSSE3; AVX; F16C and FMA; AVX2; ARM64