DirectX 12 마이크로소프트 예제 프로젝트를 분석하다가 >>, &, ~같은 bit wise연산이 빈번하게 사용되는 것을 발견하고 왜 사용하는 것인지 정리하고자 이 글을 작성한다.
ByteAddressBuffer Indices : register(t1, space0);
uint3 Load3x16BitIndices(uint offsetBytes)
{
uint3 indices;
const uint dwordAlignedOffset = offsetBytes & ~3;
const uint2 four16BitIndices = Indices.Load2(dwordAlignedOffset);
// Aligned: { 0 1 | 2 - } => retrieve first three 16bit indices
if (dwordAlignedOffset == offsetBytes)
{
indices.x = four16BitIndices.x & 0x0000ffff;
indices.y = (four16BitIndices.x >> 16) & 0x0000ffff;
indices.z = four16BitIndices.y & 0x0000ffff;
}
else // Not aligned: { - 0 | 1 2 } => retrieve last three 16bit indices
{
indices.x = (four16BitIndices.x >> 16) & 0x0000ffff;
indices.y = four16BitIndices.y & 0x0000ffff;
indices.z = (four16BitIndices.y >> 16) & 0x0000ffff;
}
return indices;
}
이 코드는 Index버퍼의 OffsetByte주소를 주면 해당 주소로부터 16비트의 크기를 가지는 Index3개를 리턴하는 HLSL함수이다.
이 코드는 ByteAddressBuffer의 특성 때문에 그렇다. ByteAddressBuffer는 memory alignment때문에 4바이트(32비트)단위로만 데이터를 Load하게 된다.
4바이트 단위인 이유는 GPU가 메모리의 데이터를 읽는 최소 단위가 4바이트이기 때문이다. 위 코드는 4바이트 단위로 읽는 제약조건 속에서 2번의 Load(8바이트)연산의 결과물에 Bit Wise연산을 통해 2바이트 Index데이터를 3개 얻는 코드라고 할 수 있다.
const uint dwordAlignedOffset = offsetBytes & ~3;
위 코드는 현재 읽고자 하는 데이터의 주소가 4단위의 숫자인지 검사하는 코드이다.(ByteAdressBuffer는 Byte의 Index로 주소를 읽는다)(ex/load(1)->1바이트 부터, load(2)->2바이트 부터)
만약, offsetBytes가 4의 단위의 숫자값을 가진다면 (& ~3)연산을 해도 값이 바뀌지 않으니 해당 데이터 Index부터 읽으면 되는 것이고,
offsetBytes가 4의 단위 숫자가 아니라면 (& ~3)연산을 하면 값이 바뀌게 되니
하위 3개 비트의 값을 0으로 만든 값을 Index로 읽은 다음에 처음 2바이트 결과물을 무시하면 된다.
if (dwordAlignedOffset == offsetBytes)
{
indices.x = four16BitIndices.x & 0x0000ffff;
indices.y = (four16BitIndices.x >> 16) & 0x0000ffff;
indices.z = four16BitIndices.y & 0x0000ffff;
}
else // Not aligned: { - 0 | 1 2 } => retrieve last three 16bit indices
{
indices.x = (four16BitIndices.x >> 16) & 0x0000ffff;
indices.y = four16BitIndices.y & 0x0000ffff;
indices.z = (four16BitIndices.y >> 16) & 0x0000ffff;
}
위 코드는 align여부에 따라 64비트의 데이터에서 16비트 Index데이터 3개를 뽑아내는 코드다. (& 0000ffff)연산으로 하위 16비트의 값만 취할 수 있고,
(>> 16)연산으로 다음 16비트 값을 가져올 수 있다.
HLSL의 ByteAddressBuffer는 GPU 하드웨어의 이유로 2바이트 단위부터 메모리를 읽을 수 있으며, 3바이트의 데이터를 추출하기 위해서 4바이트를 먼저 읽은 후 Bit Wise연산을 이용해서 3바이트를 추출하는 방식을 사용한다.