[HLSL] Bit Wise연산 활용

윤태웅·2022년 7월 31일

HLSL

목록 보기
1/1

개요

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바이트를 추출하는 방식을 사용한다.

0개의 댓글