사전 태스크 셰이더
개념

태스크 셰이더

gabury1고친 사람 github-actions[bot]

태스크 셰이더는 그래픽 카드가 모형을 그리기 직전에 이번에 그릴 조각만 골라 주는 프로그램입니다. 고른 조각을 실제로 그리는 뒤 단계, 곧 메시 셰이더를 몇 번 돌릴지도 정합니다. 안 보일 조각은 이 단계에서 버려집니다. 그래서 뒤 단계는 버려진 조각을 읽지도 계산하지도 않습니다.

쉽고 빠른 이해

태스크 셰이더는 모형 조각들을 훑어 이번에 그릴 조각만 고르는 프로그램입니다. 조각이 천 개인 건물 모형이 절반쯤 카메라 뒤에 있다면, 카메라 앞의 조각만 골라 뒤로 넘깁니다.

이게 없으면 조각마다 그리는 프로그램이 한 번씩 다 돕니다. 안 보일 조각도 일단 읽고 계산한 뒤에야 버릴 수 있습니다. 태스크 셰이더가 앞에서 걸러 주면 그 몫의 일이 아예 생기지 않습니다. 고르는 쪽은 조각마다 미리 적어 둔 작은 요약만 보므로 가볍습니다.

어떻게 도나:

  1. 그리기 명령이 태스크 셰이더를 여러 개 띄웁니다
  2. 하나마다 맡은 조각이 보이는지 확인하고, 뒤 단계를 몇 번 돌릴지 정합니다
  3. 고른 조각의 번호를 뒤 단계에 건네고, 뒤 단계는 그 조각만 그립니다

대가는 단계가 하나 늘어난다는 것입니다. 버릴 조각이 적은 장면에서는 고르는 데 드는 일이 아끼는 일보다 클 수 있습니다. 건넬 수 있는 값의 크기에도 상한이 있습니다. 이 단계를 모르는 옛 그래픽 카드에서는 돌지 않습니다.

상세

큰 물류 창고를 떠올려 봅니다. 트럭이 오기 전에 반장 한 사람이 오늘 들어온 주문서 묶음을 훑습니다. 취소된 주문은 빼고, 남은 주문마다 포장 팀을 몇 개 붙일지 정합니다. 그리고 팀들이 함께 보는 쪽지 한 장에 맡을 주문 번호를 줄지어 적어 붙입니다.

아래 본문에서 주문서 하나는 모형의 작은 조각 하나입니다. 반장은 태스크 셰이더입니다. 포장 팀은 메시 셰이더입니다. 취소된 주문을 빼는 일은 안 보일 조각을 버리는 일입니다. 쪽지는 태스크 셰이더가 메시 셰이더에 건네는 값입니다.

이 절은 먼저 태스크 셰이더가 그림 그리는 흐름의 어디쯤에서 도는지 짚습니다. 그다음 이 단계가 없을 때 무엇이 곤란한지, 하는 일 둘이 무엇인지 봅니다. 짧은 코드 하나로 그 일을 따라간 뒤, 조각을 고르는 기준과 치르는 비용으로 끝냅니다.

그림 그리는 흐름과 셰이더

화면에 보이는 입체 모형은 작은 삼각형 수만 개를 이어 붙인 것입니다. 이렇게 삼각형을 이어 붙인 모형을 메시라고 부릅니다.

삼각형의 꼭짓점 하나는 정점이라고 합니다. 정점마다 위치가 붙어 있어서, 정점 셋이 모이면 삼각형 하나가 정해집니다.

그래픽 카드(GPU, Graphics Processing Unit, 그래픽 처리 장치)는 정해진 순서대로 그림을 그립니다. 정점을 읽고, 화면 위 위치를 정하고, 삼각형이 덮는 점마다 색을 칠합니다. 이 순서 전체를 그래픽스 파이프라인이라고 부릅니다.

순서 안의 몇 단계에는 개발자가 짧은 프로그램을 적어 넣습니다. 그 프로그램을 통틀어 셰이더라고 합니다. 태스크 셰이더는 그런 셰이더 가운데 맨 앞에서 도는 것 하나입니다.

메시 셰이더와 메시렛

태스크 셰이더는 메시 셰이더와 짝으로만 씁니다. 그래서 메시 셰이더를 먼저 알아야 합니다.

메시를 삼각형 수십 개씩 작은 조각으로 나눈 하나하나를 메시렛이라고 합니다. 가까이 붙은 삼각형끼리 묶으므로 한 메시렛은 화면의 한 구역에 모입니다.

메시 셰이더는 메시렛 하나를 맡아 그 조각의 정점과 삼각형을 직접 만들어 내보내는 셰이더입니다. 앞에서 본 흐름에서 정점을 읽고 화면 위 위치를 정하던 앞쪽 단계들을 이 셰이더 하나가 대신합니다.

메시 셰이더는 스레드 여럿이 한 묶음으로 돕니다. 이 묶음을 워크그룹이라고 부릅니다. 워크그룹 하나가 일 한 덩어리를 맡고, 안의 스레드들이 그 덩어리를 나눠 처리합니다. 태스크 셰이더도 똑같이 워크그룹 단위로 돕니다.

태스크 셰이더가 도는 단계

메시 셰이더를 쓰는 흐름은 그리기 명령 하나로 시작합니다. 이 명령은 워크그룹을 몇 개 띄울지를 숫자로 받습니다.

태스크 셰이더를 붙이지 않으면 그 숫자만큼 메시 셰이더 워크그룹이 바로 뜹니다. 태스크 셰이더를 붙이면 그 숫자만큼 태스크 셰이더 워크그룹이 먼저 뜹니다. 메시 셰이더 워크그룹을 몇 개 띄울지는 태스크 셰이더가 정합니다.

flowchart TD
    A["그리기 명령 · 워크그룹 수를 받는다"] --> B["태스크 셰이더 · 붙였을 때만"]
    B --> C["메시 셰이더 · 조각 하나마다"]
    A -.->|"태스크 셰이더를 안 붙였을 때"| C
    C --> D["삼각형이 덮는 화면의 점 고르기"]
    D --> E["점마다 색 칠하기"]

메시 셰이더가 삼각형을 내보낸 뒤로는 예전 흐름과 같습니다. 먼저 삼각형이 덮는 화면의 점, 곧 픽셀을 골라냅니다. 이 일을 래스터화라고 부릅니다.

그다음 골라낸 픽셀마다 짧은 프로그램이 한 번씩 돌며 색을 정합니다. 이 프로그램을 프래그먼트 셰이더라고 합니다.

프로그램이 그래픽 카드에 그리기를 시킬 때는 그래픽 API(Application Programming Interface, 애플리케이션 프로그래밍 인터페이스)를 거칩니다. 그래픽 API 는 여럿입니다. 태스크 셰이더 단계의 이름도 API 마다 다릅니다.

Vulkan과 OpenGL에서는 태스크 셰이더라고 부릅니다. 마이크로소프트가 만든 Direct3D에서는 같은 단계를 증폭 셰이더라고 부릅니다.

없으면 곤란한 대목

카메라에 안 보일 도형을 그리기 전에 버리는 일을 컬링이라고 합니다. 버린 만큼 계산이 줄어드니 큰 장면일수록 중요합니다.

태스크 셰이더가 없으면 메시렛마다 메시 셰이더 워크그룹이 하나씩 뜹니다. 메시 셰이더가 제 조각이 안 보인다는 것을 알아채고 삼각형을 하나도 안 내보낼 수는 있습니다. 하지만 그때는 이미 워크그룹 하나가 떠서 조각을 읽은 뒤입니다.

그렇다고 CPU(Central Processing Unit, 중앙 처리 장치)가 미리 골라 주기도 어렵습니다. 모형 하나를 통으로 버리는 판단은 CPU 도 합니다. 하지만 메시렛 수천 개를 하나하나 확인해서 그리기 명령을 따로 만들면 그 일이 무겁습니다.

태스크 셰이더는 이 판단을 그래픽 카드 안으로 가져옵니다. 메시렛 수천 개를 그래픽 카드가 나란히 확인하고, 보이는 조각에만 메시 셰이더를 띄웁니다. 이렇게 그릴 것을 그래픽 카드가 스스로 정하는 방식을 GPU 주도 렌더링이라고 부릅니다.

태스크 셰이더도 워크그룹으로 떠서 조각을 봐야 합니다. 그래도 메시 셰이더를 다 띄우는 것보다 쌉니다. 태스크 셰이더는 조각의 정점을 읽지 않고, 조각마다 미리 구해 둔 작은 요약값만 봅니다. 조각을 감싸는 공 하나 같은 값입니다.

게다가 태스크 셰이더 워크그룹 하나가 조각 수십 개를 한꺼번에 봅니다. 조각마다 워크그룹이 하나씩 뜨는 메시 셰이더와 달리, 뜨는 워크그룹 수부터 적습니다.

하는 일 둘

태스크 셰이더 워크그룹 하나가 하는 일은 둘입니다. 메시 셰이더 워크그룹을 몇 개 띄울지 정하는 일, 그리고 그 워크그룹들에게 건넬 값을 채우는 일입니다.

첫째는 개수입니다. 워크그룹은 끝나기 직전에 개수를 한 번 알립니다. 0 을 알리면 메시 셰이더가 하나도 안 뜨고, 맡은 조각은 거기서 버려집니다.

둘째는 건넬 값입니다. 이 값을 페이로드라고 부릅니다. 대개 고른 메시렛의 번호를 담습니다. 메시 셰이더는 그 번호로 자기가 맡을 조각을 찾아 읽습니다.

태스크 셰이더 워크그룹 하나는 조각 여럿을 맡을 수 있습니다. 맡은 조각을 하나씩 확인해 보이는 것의 번호만 페이로드에 모읍니다. 그리고 모은 번호 수만큼 메시 셰이더 워크그룹을 띄웁니다.

페이로드 하나는 태스크 셰이더 워크그룹 하나가 씁니다. 그 워크그룹이 띄운 메시 셰이더 워크그룹들은 모두 같은 페이로드를 읽기만 합니다. 아래 그림은 태스크 셰이더 워크그룹 둘이 조각을 셋씩 맡아 저마다 다른 개수를 알린 모습입니다. 그림에서는 줄여서 태스크 워크그룹, 메시 워크그룹이라고 적었습니다.

flowchart TD
    subgraph T0["태스크 워크그룹 0 · 조각 0·1·2 가 보인다"]
        P0["페이로드 · 조각 번호 0·1·2"]
    end
    subgraph T1["태스크 워크그룹 1 · 조각 3·4·5 가 안 보인다"]
        P1["개수 0 을 알린다"]
    end
    P0 --> M0["메시 워크그룹 0번 · 페이로드 첫 칸"]
    P0 --> M1["메시 워크그룹 1번 · 페이로드 둘째 칸"]
    P0 --> M2["메시 워크그룹 2번 · 페이로드 셋째 칸"]

태스크 워크그룹 0 은 메시 워크그룹 셋을 띄웠습니다. 셋은 0 번부터 차례로 제 워크그룹 번호를 받습니다. 저마다 그 번호로 페이로드의 몇 번째 칸을 읽을지 정해 자기 조각 번호를 찾습니다.

태스크 워크그룹 1 은 0 을 알렸습니다. 조각 3·4·5 를 맡을 메시 셰이더는 아예 뜨지 않습니다.

그리는 순서도 지켜집니다. 앞 번호 태스크 셰이더 워크그룹이 띄운 메시 셰이더의 삼각형이 뒤 번호의 것보다 먼저 화면에 그려집니다. 나란히 돌아도 결과가 뒤섞이지 않는다는 뜻입니다.

짧은 코드 예

아래는 워크그룹 하나가 메시렛 하나를 맡는 가장 작은 태스크 셰이더입니다. 셰이더 언어 GLSL(OpenGL Shading Language, OpenGL 셰이딩 언어)로 적었습니다. Vulkan 에서 쓰는 형식입니다. 3번 메시렛을 맡은 워크그룹이 돌 때의 값을 오른쪽에 적었습니다.

glsl
#version 450
#extension GL_EXT_mesh_shader : require

layout(local_size_x = 1) in;

taskPayloadSharedEXT uint meshletId;

bool visible(uint id) {
  return id % 2 == 0; // 짝수만 보인다
}

void main() {
  uint id = gl_WorkGroupID.x;   // 3
  meshletId = id;               // 3
  uint n = visible(id) ? 1u : 0u; // 0
  EmitMeshTasksEXT(n, 1, 1);    // 0개
}

첫 두 줄은 이 언어에서 메시 셰이더 기능을 켜는 선언입니다. 태스크 셰이더는 메시 셰이더와 함께 나중에 더해진 기능이라 켜고 써야 합니다.

local_size_x = 1 은 워크그룹 하나에 스레드를 몇 개 묶을지 정합니다. 이런 개수는 가로·세로·깊이 세 칸으로 적습니다. _x 는 그 가운데 가로 칸이라는 뜻입니다. 적지 않은 칸은 1 로 칩니다.

이 예는 스레드가 하나뿐입니다. 그래서 워크그룹 하나가 곧 메시렛 하나입니다.

taskPayloadSharedEXT 가 붙은 변수가 페이로드입니다. 여기서는 메시렛 번호 하나만 담습니다.

visible 은 조각이 보이는지 확인하는 함수입니다. 진짜 판단은 뒤 소절의 기준으로 합니다. 이 예에서는 짝수 번호만 보인다고 쳤습니다.

gl_WorkGroupID.x 는 지금 도는 워크그룹 번호의 가로 칸입니다. 그리기 명령이 워크그룹을 천 개 띄웠다면 0 부터 999 까지가 붙습니다. 이 예는 그 번호를 곧 메시렛 번호로 씁니다.

n 은 보이면 1, 안 보이면 0 입니다. 1u 의 u 는 부호 없는 정수라는 표시입니다.

EmitMeshTasksEXT 가 개수를 알리는 함수입니다. 이 개수도 가로·세로·깊이 세 칸으로 적습니다. 이 예는 첫 칸에 n 을 넣고 나머지 두 칸은 1 로 둡니다. 띄우는 워크그룹 수는 세 칸을 곱한 값입니다.

3 은 홀수라 n 이 0 입니다. 그래서 이 조각을 맡을 메시 셰이더는 안 뜹니다. 4번 워크그룹이었다면 n 이 1 이 되어 메시 셰이더 워크그룹 하나가 뜹니다.

이 함수는 워크그룹의 모든 스레드가 같은 길을 지나며 한 번 불러야 합니다. 부르고 나면 그 워크그룹은 거기서 끝납니다. 그래서 if 로 가지를 쳐 두 번 부르지 않고, 개수를 먼저 n 에 담은 뒤 한 번만 부릅니다.

메시 셰이더 쪽은 같은 이름의 페이로드를 선언해 읽습니다. 4번 워크그룹이 띄운 메시 셰이더라면 이렇게 됩니다.

glsl
taskPayloadSharedEXT uint meshletId;

void main() {
  uint id = meshletId;          // 4
  // id 번 메시렛의 정점을 읽는다
}

메시 셰이더는 받은 번호로 버퍼에서 그 메시렛의 정점을 찾아 읽습니다.

이 예는 워크그룹 하나에 조각 하나를 맡겼습니다. 실무에서는 앞 소절 그림처럼 워크그룹 하나가 조각 여럿을 맡는 꼴이 흔합니다. 워크그룹에 스레드를 수십 개 묶고, 스레드마다 조각 하나를 확인합니다. 보이는 번호만 페이로드에 모아 그 개수를 알립니다.

조각을 고르는 기준

visible 함수가 내리는 판단은 여럿입니다. 셋 다 메시렛마다 미리 계산해 둔 요약값을 씁니다.

기준 무엇을 보고 버리나
절두체 컬링 조각이 카메라가 보는 범위 밖에 있나
후면 컬링 조각의 삼각형이 전부 카메라에 등을 돌렸나
오클루전 컬링 조각이 앞에 선 다른 물체에 가려졌나

카메라가 보는 범위는 꼭대기가 잘린 피라미드 모양이라 절두체라고 부릅니다. 메시렛을 감싸는 공 하나를 미리 구해 두면, 그 공이 절두체 밖인지만 봐도 조각 전체를 버릴 수 있습니다.

삼각형에는 앞면과 뒷면이 있습니다. 카메라는 대개 앞면만 그립니다. 한 메시렛의 삼각형들이 향하는 방향을 원뿔 하나로 감싸 둡니다. 그 원뿔이 통째로 카메라 반대쪽을 향하면 조각을 버립니다.

가려짐은 바로 앞 화면을 그린 결과로 확인합니다. 앞 화면을 그릴 때 점마다 카메라에서 가장 가까운 물체까지의 거리를 남겨 둡니다. 조각을 감싸는 공이 그 거리보다 멀면 가려졌다고 보고 그리지 않습니다.

버리지 않고 고르는 기준도 있습니다. 멀리 있는 물체는 삼각형이 적은 조각으로 그려도 차이가 안 보입니다. 거리에 따라 거친 조각과 촘촘한 조각 가운데 하나를 고르는 일을 레벨 오브 디테일이라고 부릅니다. 이 판단도 태스크 셰이더가 맡을 수 있습니다.

삼각형을 불리는 쓰임

태스크 셰이더는 조각 하나를 보고 메시 셰이더 워크그룹을 여럿 띄울 수도 있습니다. 그러면 들어온 조각보다 많은 삼각형이 나옵니다. 증폭 셰이더라는 다른 이름이 이 쓰임에서 왔습니다.

풀밭이 그런 예입니다. 땅 조각 하나를 보고, 가까우면 풀잎을 그리는 메시 셰이더를 많이 띄우고 멀면 적게 띄웁니다. 풀잎 정점을 메모리에 쌓아 두지 않고 그릴 때 만들어 냅니다.

쓰는 경우와 치르는 비용

태스크 셰이더가 이득을 내는 장면은 메시렛이 많고 그중 안 보이는 것이 많은 장면입니다. 넓은 도시나 숲처럼 카메라 뒤와 벽 뒤에 숨는 부분이 큰 장면이 그렇습니다.

반대로 모형이 작고 거의 다 보이는 장면에서는 버릴 것이 적습니다. 고르는 단계 하나가 늘어난 몫만 남으니 태스크 셰이더를 안 붙이는 편이 가볍습니다.

비용은 셋입니다. 첫째, 페이로드 크기에 상한이 있어 큰 값을 건넬 수 없습니다. 번호처럼 작은 값을 건네고 큰 데이터는 메시 셰이더가 버퍼에서 직접 읽게 합니다.

둘째, 조각마다 요약값을 미리 만들어 두어야 합니다. 감싸는 공과 방향을 감싸는 원뿔은 모형을 메시렛으로 나눌 때 함께 계산해 둡니다.

셋째, 돌릴 수 있는 곳이 좁습니다. 태스크 셰이더는 메시 셰이더와 함께 들어온 기능이라 비교적 새 그래픽 카드와 그래픽 API 에만 있습니다. 옛 카드까지 지원하려면 예전 흐름으로 그리는 길을 하나 더 유지해야 합니다.

관련 항목

태스크 셰이더가 짝을 이루는 처리 단계

메시 셰이더 · 래스터화 · 프래그먼트 셰이더 · 픽셀 셰이더 · 출력 병합기 · 그래픽스 파이프라인

메시 셰이더 흐름이 대신하는 옛 앞쪽 단계

입력 어셈블러 · 정점 셰이더 · 테셀레이션 · 테셀레이션 제어 셰이더 · 테셀레이션 평가 셰이더 · 지오메트리 셰이더

태스크 셰이더가 고르고 건네는 데이터

메시렛 · 메시 · 정점 · 삼각형 · 프리미티브 · 페이로드 · 버퍼 · 경계 구 · 법선 원뿔

태스크 셰이더가 조각을 버리는 기법

컬링 · 절두체 컬링 · 후면 컬링 · 오클루전 컬링 · 법선 원뿔 컬링 · 계층적 Z 버퍼 · 레벨 오브 디테일

태스크 셰이더가 빌려 온 계산 방식

컴퓨트 셰이더 · 워크그룹 · 스레드 그룹 · 공유 메모리 · 디스패치 · 간접 그리기 · GPU 주도 렌더링

태스크 셰이더를 규정하는 그래픽 API와 셰이더 언어

API · Vulkan · Direct3D · Direct3D 12 · OpenGL · Metal · GLSL · HLSL · SPIR-V

태스크 셰이더를 돌리는 하드웨어

GPU · CPU · 셰이더 코어 · 병렬 처리 · 비디오 메모리

태스크 셰이더로 그리는 도형 생성 기법

절차적 생성 · 지형 렌더링 · 풀 렌더링 · 머리카락 렌더링 · 파티클 시스템

태스크 셰이더가 속하는 상위 분류

그래픽스 · 렌더링 · 실시간 렌더링 · 셰이더 · 지오메트리 처리 · 3D 그래픽스

다른 이름: task shader · 증폭 셰이더 · amplification shader · 태스크 쉐이더