[Unreal Engine] Compute Shader Basic 2

이매·약 9시간 전

Unreal Graphics & Rendering

목록 보기
14/14
post-thumbnail

참고 자료
Understanding C++ Compute Shader Dispatch and RDG in Unreal Engine - Ghislain Girardot
Compute Shader Overview - Microsoft

2. Thread의 식별 ID

이전 글에서는 CPU가 Dispatch()를 호출하고, GPU가 Thread와 Thread Group 단위로 병렬 계산을 수행하는 구조를 알아보았다.

그렇다면 실제 Compute Shader 내부에서는 각각의 Thread가 자신이 어떤 작업을 수행해야 하는지 어떻게 구분할 수 있을까?
예를 들어 다음과 같은 코드가 있다고 가정해보자.

[numthreads(16,16,1)]

그리고 CPU에서

Dispatch(64,64,1)

를 호출했다면 GPU에서는 1024 X 1024의 Thread(약 100만 개)가 동시에 실행된다.

이때 각 Thread는

  • 자신이 어느 위치의 데이터를 처리하는지
  • 어떤 픽셀 또는 버퍼 인덱스를 담당하는지
  • 현재 어떤 Thread Group에 속해있는지

알아야 한다.
Compute Shader에서는 이를 위해 GPU 시스템이 자동으로 제공하는 여러 가지 식별 ID(Semantic)를 사용한다.

대표적으로 다음과 같은 값들이 존재한다.

  • SV_GroupID
  • SV_GroupThreadID
  • SV_DispatchThreadID
  • SV_GroupIndex

이 값들은 현재 실행 중인 Thread의 위치와 역할을 식별하기 위한 정보이며, Compute Shader의 대부분의 계산 로직은 이 값들을 기반으로 작성된다.

2-1. SV_GroupID

SV_GroupID는 현재 실행 중인 Thread가 어떤 Thread Group에 속해있는지를 나타내는 값이다.
예를 들어 다음과 같은 코드가 있다고 가정해보자.

Dispatch(4,4,1)

이 경우, GPU 전체에는 4 × 4개의 Thread Group이 생성된다.
그리고 SV_GroupID는 (0,0,0) ~ (3,3,0)의 범위를 가지게 된다.

2-2. SV_GroupThreadID

SV_GroupThreadID는 현재 Thread가 자신이 속한 Thread Group 내부에서 어느 위치에 있는지를 나타내는 값이다.
앞서 SV_GroupID가 현재 어떤 Group에 속해있는가를 의미했다면, SV_GroupThreadID는 현재 Group 내부에서 몇 번째 Thread인가를 의미한다.

예를 들어 다음과 같은 코드가 있다고 가정해보자.

[numthreads(8,8,1)]

이 경우 하나의 Thread Group 내부에는 8 × 8 × 1 = 64 Threads가 생성된다.
그리고 SV_GroupThreadID는 (0,0,0) ~ (7,7,0)의 범위를 가지게 된다.

2-3. SV_DispatchThreadID

SV_DispatchThreadID는 Compute Shader 전체 실행 공간 기준에서 현재 Thread의 위치를 나타내는 값이다.
앞서 SV_GroupID는 현재 Thread가 어떤 Thread Group에 속해있는지를 나타내고, SV_GroupThreadID는 현재 Group 내부에서 몇 번째 Thread인지를 나타낸다고 설명했다.

하지만 실제 Compute Shader에서는 단순히 Group 내부 위치만 아는 것이 아니라 전체 실행 공간 기준에서 현재 Thread가 어떤 데이터를 처리해야 하는지알아야 하는 경우가 대부분이다.
이를 위해 사용되는 값이 바로 SV_DispatchThreadID이다.

SV_DispatchThreadID는 현재 실행 중인 Thread의 전역(Global) 좌표를 의미하며

SV_DispatchThreadID=SV_GroupID×numthreads+SV_GroupThreadIDSV\_DispatchThreadID = SV\_GroupID \times numthreads + SV\_GroupThreadID

공식을 통해 계산된다.
예를 들어 다음과 같이 Thread와 Thread Group이 있다고 가정해보자.

[numthreads(8,8,1)]
Dispatch(4,2,1)

그러면 하나의 Group 내부에는 8 × 8개의 Thread가 존재하고 X 방향으로 4개, Y 방향으로 2개의 Group이 생성된다.
따라서 전체 실행 공간 기준으로는 32 × 16크기의 Thread 공간이 만들어지며, SV_DispatchThreadID는 (0,0,0) ~ (31,15,0) 범위의 전역 좌표를 가지게 된다.

2-4. SV_GroupIndex

SV_GroupIndex는 현재 Thread가 자신이 속한 Thread Group 내부에서 몇 번째 Thread인지를 1차원 인덱스 형태로 나타내는 값이다.
앞서 SV_GroupThreadID는 (x, y, z)형태의 3차원 좌표를 통해 Group 내부 위치를 표현했다. 하지만 실제 Compute Shader에서는 Group 내부 데이터를 배열 형태로 접근해야 하는 경우가 많으며, 이때는 3차원 좌표보다 하나의 선형 인덱스 값이 더 효율적으로 사용된다.

이를 위해 제공되는 값이 바로 SV_GroupIndex이다.
즉 SV_GroupIndex는 다차원 SV_GroupThreadID를 1차원 값으로 변환한 Flattened Index라고 이해할 수 있다.

SV_GroupIndex는 내부적으로 다음과 같은 공식을 통해 계산된다.

SV_GroupInde=SV_GroupThreadID.z×dimx×dimy+SV_GroupThreadID.y×dimx+SV_GroupThreadID.xSV\_GroupInde = SV\_GroupThreadID.z \times dimx \times dimy + SV\_GroupThreadID.y \times dimx + SV\_GroupThreadID.x

여기서 dimx와 dimy는 Threads에 지정한 Group 크기를 의미한다.


번외 퀴즈

다음과 같은 Compute Shader가 있다고 가정해보자.

[numthreads(4,4,1)]

그리고 CPU에서 다음과 같이 호출하였다.

Dispatch(3,2,1)

현재 어떤 Thread의 정보가 다음과 같다고 가정하자.

SV_GroupID = (2,1,0)
SV_GroupThreadID = (1,3,0)

이때 다음의 값을 각각 구해보자.

1. 전체 GPU에는 총 몇 개의 Thread Group이 생성되는가?

2. 하나의 Thread Group 내부에는 총 몇 개의 Thread가 존재하는가?

3. 현재 Thread의 SV_DispatchThreadID 값은 얼마인가?

4. 현재 Thread의 SV_GroupIndex 값은 얼마인가?


💡 정답

  1. 3 × 2 × 1 = 6 Groups
  1. 4 × 4 × 1 = 16 Threads
  1. 1) (2, 1, 0) * (4, 4, 1) + (1, 3, 0)
    2) (8, 4, 0) + (1, 3, 0)
    3) (9, 7, 0) = SV_DispatchThreadID
  1. 1) (0 * 4 * 4) + (3 * 4) + 1
    2) 0 + 12 + 1
    3) 13 = SV_GroupIndex
profile
언리얼 엔진 주니어(신입) 개발자 | 소설 쓰는 취준 개발자

0개의 댓글