Graphics Programming
컴퓨트 셰이더(Compute Shader) 본문
※ 예제 코드들은 OpenGL Super Bible에서 가져온 것입니다.
- 컴퓨트 셰이더 스테이지는 별개의 파이프라인. OpenGL의 다른 셰이더들과 단절되어 있다
- 고정된 입출력이 없다
- 프로그래밍 관점에서는 다른 셰이더들과 같다
- GLSL로 작성, 셰이더 개체로 표현, 프로그램 개체에 링크됨
- 컴퓨트 셰이더는 다른 셰이더들과 섞일 수 없다
- 이미 정점, 단편 셰이더가 있는 프로그램에 컴퓨트 셰이더를 붙일 수 없음 (링크 실패)
- 링크된 프로그램은 컴퓨트 셰이더만 포함하거나 그래픽스 셰이더들(정점, 테셀레이션, 기하, 단편)만을 포함한다
// 아무것도 안 하는 컴퓨트 셰이더
#version 430 core
layout(local_sizee_x = 32, local_size_y = 32) in;
void main() {}
// 컴퓨트 셰이더 실행
void glDispatchCompute(GLuint num_groups_x, num_groups_y, num_groups_z);
void glDispatchComputeIndirect(GLintptr indirect);
- indirect: 버퍼 개체 내에서의 오프셋. glDispatchCompute()에 전달할 수 있는 매개변수들이 포함된다
워크 그룹
- 컴퓨트 셰이더는 워크 그룹 단위로 실행된다
- glDispatchCompute[Indirect]() 한 번 호출 시
- 하나의 글로벌 워크 그룹이 GL에 전송된다
- 글로벌 워크 그룹은 로컬 워크 그룹들로 나눠진다
- 로컬의 x, y, z 방향 크기는 num_grous_x/y/z (기본값: 1/1/1)
- 워크 그룹은 워크 아이템들의 3D 블록
- 각 워크 아이템마다 컴퓨트 셰이더 실행
- 최대 크기 : GL_MAX_COMPUTE_WORK_GROUP_SIZE_INVOCATIONS (최소 1024)
- 각 방향의 크기 (x, y, z) >= (1024, 1024, 64)
- 로컬 워크 그룹 크기 알아내기
- int size[3];
- glGetProgramiv(program, GL_COMPUTE_WORKGROUP_SIZE, size);
입출력
- 내장된 출력은 없다
- 다른 셰이더들과 달리 사용자 정의 출력을 선언할 수 없다
- 내장된 입력 변수들
- uvec3 gl_LocalInvocationID
- uvec3 gl_WorkGroupSize
- uvec3 gl_NumWorkGroups
- uvec3 gl_WorkGroupID
- uvec3 gl_GlobalInvocationID = gl_WorkGroupID * gl_WorkGroupSize + gl_LocalIinvocationID
- 데이터는 셰이더 코드에서 메모리에 직접 저장해야 한다
- Shader Storage Block에 기록
- image 함수들
- atomic counter 증감
// 이미지 변수를 이용한 출력
#version 430 core
layout(local_size_x = 32, local_size_y = 32) in;
layout(binding = 0, rgba32f) uniform image2D img_output;
layout(binding = 1) uniform image2D img_output;
void main() {
vec4 texel;
ivec2 p = ivec2(gl_GlobalInvocationID.xy);
texel = imageLoad(img_input, p);
texel = vec4(1.0) - texel;
imageStore(img_output, p, texel);
}
glBindImageTexture(0, tex_input, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F);
glBindImageTexture(1, tex_input, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F);
glDispatchCompute(IMAGE_WIDTH/32, IMAGE_HEIGHT/32);
동기화
- 플로우 동기화: barrier()
- 같은 워크 그룹 내 모든 invocation이 barrier() 호출 시점에 다다를 때까지 대기
- 메모리 동기화
- memoryBarrier() : 지금까지의 image, buffer, shared variable 쓰는 연산들에 모두 배리어 적용
- memoryBarrierShared(), memoryBarrierImage(), memoryBarrierBuffer() : 각각에 대해서만 적용
- groupMemoryBarrier() : 같은 워크 그룹 내의 모든 invocation에 대해 동기화