Quick Comparison
barrier 비트는 다음 소비 방식에 맞춥니다. SSBO에 썼다는 이유만으로 이후 모든 읽기에 SHADER_STORAGE 비트 하나가 맞는 것은 아닙니다.
| 해결할 문제 | 사용할 개념 | 대신하지 못하는 것 |
|---|---|---|
| shader 쓰기를 다음 GL 소비자가 관찰 | glMemoryBarrier | CPU의 완료 대기 |
| 특정 지점까지 GPU 완료 확인 | fence + wait | 잘못된 메모리 접근의 가시성 규약 |
| 명령을 실행 쪽으로 밀어 넣기 | glFlush | 완료 보장 |
| 앞 GL 작업 전체 완료까지 CPU 대기 | glFinish | 좋은 기본 프레임 스케줄 |
소비자별 가시성
| 이후 소비자 | 대표 barrier 비트 |
|---|---|
| 정점 속성 입력 | VERTEX_ATTRIB_ARRAY |
| 요소 인덱스 | ELEMENT_ARRAY |
| 텍스처 샘플링 | TEXTURE_FETCH |
| image load/store | SHADER_IMAGE_ACCESS |
| SSBO 읽기·쓰기 | SHADER_STORAGE |
| 간접 draw/dispatch 명령 | COMMAND |
| 버퍼의 API 읽기·갱신 경로 | BUFFER_UPDATE |
표는 대표 경로이며 모든 비트를 나열한 표가 아닙니다. GL 4.3 compute가 SSBO에 정점 자료를 쓴 뒤 draw가 정점 버퍼로 읽는다면 다음처럼 연결합니다.
glUseProgram(computeProgram);
glDispatchCompute(groupCount, 1, 1);
glMemoryBarrier(GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT);
glUseProgram(renderProgram);
glBindVertexArray(vao);
glDrawArrays(GL_POINTS, 0, vertexCount);두 프로그램·VAO·공유 저장소가 준비되어 있고 groupCount와 입력 범위가 맞다는 전제입니다. 셰이더에서 배열 범위를 넘는 접근을 barrier가 고쳐 주지는 않습니다. ALL_BARRIER_BITS는 원인을 찾거나 보수적 선택을 할 때 사용할 수 있지만 정확한 의존성을 설명하는 대체물로 쓰지 않습니다.
Fence와 대기 결과
3.2 또는 ARB_sync에서는 마지막 소비 뒤 glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0)으로 fence를 만들 수 있습니다. 반환 0은 실패입니다. client wait의 timeout 단위는 나노초이고, 0이면 기다리지 않고 상태만 확인합니다.
- ALREADY_SIGNALED·CONDITION_SATISFIED: 완료 조건 충족.
- TIMEOUT_EXPIRED: 미완료이므로 자원 재사용을 미룹니다.
- WAIT_FAILED: 오류를 처리하고 완료로 간주하지 않습니다.
glWaitSync는 이후 GPU 명령이 기다리는 server wait이며 CPU를 완료까지 멈추는 client wait와 다릅니다. 공유 컨텍스트에서는 producer 명령과 fence가 실제로 제출되도록 producer의 flush 책임도 정합니다. consumer에서 기다린다고 producer의 미제출 명령이 자동으로 실행되는 것은 아닙니다. 사용이 끝난 sync는 삭제합니다.
대기 결과에 따라 구간 재사용하기
동일 컨텍스트에서 사용한 구간의 마지막 draw 뒤 fence를 만들고, 나중 프레임에 한 번씩 상태를 확인합니다. 이 예제는 GL 3.3에서도 가능한 sync 경로이며 루프를 돌며 바쁘게 기다리지 않습니다.
// 마지막 소비 명령 뒤 한 번 생성하고 다음 프레임까지 보관합니다.
GLsync fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
if (fence == nullptr) {
// 생성 실패: 완료를 확인할 수 없으므로 해당 구간을 재사용하지 않습니다.
}// fence가 유효한 경우, 나중 프레임에서 실행합니다.
GLenum state = glClientWaitSync(fence, GL_SYNC_FLUSH_COMMANDS_BIT, 0);
if (state == GL_ALREADY_SIGNALED || state == GL_CONDITION_SATISFIED) {
glDeleteSync(fence);
fence = nullptr;
// 이 구간의 이전 GPU 소비가 끝났으므로 재사용할 수 있습니다.
} else if (state == GL_TIMEOUT_EXPIRED) {
// fence를 보관하고 다른 구간을 사용하거나 다음 프레임에 다시 확인합니다.
} else {
// GL_WAIT_FAILED: 오류/컨텍스트 손실을 처리하고 완료로 간주하지 않습니다.
}GL_SYNC_FLUSH_COMMANDS_BIT는 같은 컨텍스트에서 아직 제출되지 않은 명령이 있을 때 제출을 요청할 수 있습니다. 다른 컨텍스트의 producer를 대신 flush하는 해결책은 아닙니다. server wait의 실제 형태는 glWaitSync(fence, 0, GL_TIMEOUT_IGNORED)이며, 이후 GPU 소비를 기다리게 할 뿐 CPU가 그 자리에서 완료를 확인한 것은 아닙니다.
영속 매핑의 GPU→CPU 읽기
GPU가 영속 매핑된 저장소에 쓴 뒤 CPU가 읽는 경우에는 쓰기 완료와 CPU 가시성을 모두 확보합니다. 비일관 매핑은 GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT의 glMemoryBarrier를 먼저 호출하고 fence를 넣어 client wait의 완료 결과를 확인합니다. coherent 매핑도 fence와 완료 확인은 필요하지만 이 가시성 barrier 요구는 다릅니다. glFinish로 완료를 기다리는 대안은 전체 대기를 유발할 수 있습니다. timeout이나 reset을 정상 완료로 처리하지 않습니다.
일관성·실행 순서·버전
GLSL의 coherent 한정자, memoryBarrier, barrier는 같은 역할이 아닙니다. 작업 그룹 내부 실행 합류는 Compute, draw/dispatch 사이 가시성은 이 카드의 API 경로입니다. persistent mapping의 coherent 설정도 GPU가 읽는 구간을 CPU가 덮어써도 된다는 허가가 아닙니다.
glMemoryBarrier는 4.2 코어 경로이며 SHADER_STORAGE 같은 추가 비트는 4.3 조건입니다. 일반 FBO 출력 후 다음 패스의 샘플링은 무조건 이 barrier가 필요한 작업으로 분류하지 않습니다. 패스 피드백과 비일관 shader 메모리 쓰기를 구분합니다.
참고 링크
6 sources