Quick Comparison
버퍼 크기를 다시 정할 때는 Data, 기존 영역에 데이터를 넣을 때는 SubData, CPU 주소로 접근할 때는 매핑을 선택합니다. 불변 저장소는 내용 불변이 아니라 할당 조건을 고정한다는 뜻입니다.
| 작업 | 3.3 기본 경로 | 경계 |
|---|---|---|
| 할당·재할당 | glBufferData | 이전 저장소 내용 보존을 가정하지 않음 |
| 일부 덮어쓰기 | glBufferSubData | offset + size가 할당 크기 이내 |
| 버퍼 간 복사 | glCopyBufferSubData | 읽기·쓰기 대상과 겹침 조건 확인 |
| CPU 직접 접근 | glMapBufferRange | 접근 플래그·범위·반환 포인터·unmap |
할당의 계약
// GL 3.3 Core, 생성된 buffer를 작업 대상으로 선택합니다.
glBindBuffer(GL_ARRAY_BUFFER, buffer);
const float values[] = {1.0f, 2.0f, 3.0f, 4.0f};
glBufferData(GL_ARRAY_BUFFER, sizeof(values), values, GL_DYNAMIC_DRAW);
const float replacement = 9.0f;
glBufferSubData(GL_ARRAY_BUFFER, sizeof(float), sizeof(float), &replacement);결과는 두 번째 float만 바뀐 배열입니다. offset과 size는 바이트입니다. 초기 버퍼 저장소 크기는 0이고, glBufferData(..., nullptr, ...)는 공간만 준비하며 값을 0으로 채우지 않습니다. GL이 참조하는 데이터의 초기값이 필요하면 직접 업로드하거나 지원되는 지우기 경로를 사용합니다.
| 인수·속성 | 가능한 값과 의미 | 변경 조건·증상 |
|---|---|---|
| target | ARRAY·ELEMENT·UNIFORM·COPY 등 버전별 target | 해당 바인딩에 유효 객체가 필요 |
| size | 0 이상 바이트 크기 | 음수는 오류, 큰 할당은 메모리 부족 가능 |
| usage | STREAM/STATIC/DYNAMIC × DRAW/READ/COPY | 접근 성향 힌트이며 쓰기 금지 권한이 아님 |
| subdata offset/size | 저장소 내부 바이트 구간 | 범위를 넘으면 오류 |
| map access | READ/WRITE와 허용 보조 플래그 | 실제 읽기·쓰기가 플래그와 일치해야 함 |
STATIC_DRAW 버퍼를 갱신했다고 문법상 금지가 되는 것은 아닙니다. 반대로 DYNAMIC_DRAW를 썼다고 GPU와 CPU 사이의 대기가 자동으로 사라지지도 않습니다.
매핑과 해제
glMapBufferRange는 범위와 접근 플래그를 받아 CPU 포인터를 반환합니다. 실패한 null 포인터를 역참조하지 않습니다. 일반 매핑은 사용 후 glUnmapBuffer를 호출하고 반환 상태가 false이면 데이터가 유효하다고 가정하지 않습니다. 매핑 범위를 벗어난 쓰기는 GL이 대신 보호해 주지 않습니다.
READ 없이 반환 메모리를 읽거나 WRITE 없이 쓰지 않습니다. 비영속 매핑 중에는 해당 저장소를 GPU 명령에 사용하는 데 제약이 있습니다. draw 전에 unmap하는 기본 경로부터 사용합니다. 반복 갱신의 대기·orphaning·persistent mapping은 동적 스트리밍에서 다룹니다.
범위 하나를 매핑해서 바꾸기
앞의 float 네 개가 들어 있는 가변 buffer의 두 번째 값을 7로 바꿉니다. 일반 매핑이므로 GPU가 다시 사용할 때는 먼저 unmap합니다. <cstring>의 memcpy를 사용하면 반환 주소에 특정 C++ 타입 정렬을 추가로 가정하지 않고 값을 복사할 수 있습니다.
glBindBuffer(GL_ARRAY_BUFFER, buffer);
void* mapped = glMapBufferRange(GL_ARRAY_BUFFER, sizeof(GLfloat), sizeof(GLfloat),
GL_MAP_WRITE_BIT);
if (mapped != nullptr) {
const GLfloat value = 7.0f;
std::memcpy(mapped, &value, sizeof(value));
if (glUnmapBuffer(GL_ARRAY_BUFFER) == GL_FALSE) {
// 내용이 손상된 경우 CPU 원본으로 다시 채우거나 이번 사용을 중단합니다.
}
}READ 접근에는 invalidate·UNSYNCHRONIZED를 함께 지정할 수 없습니다. FLUSH_EXPLICIT는 WRITE가 있어야 하며, 명시한 경우 수정한 매핑 범위를 flush해야 합니다. 단순한 한 값 수정은 SubData가 더 짧으므로 매핑이 필요할 때 이 경로를 선택합니다.
불변 저장소와 DSA
버퍼 객체는 1.5, 범위 매핑은 3.0, 버퍼 간 복사는 3.1에 코어로 들어왔습니다. 4.4의 glBufferStorage는 저장소를 한 번 할당하고 flags로 이후 접근을 정합니다. SubData 갱신이 필요하면 GL_DYNAMIC_STORAGE_BIT, 매핑은 해당 MAP 플래그가 필요합니다. 한 번 불변으로 만든 저장소에 BufferData를 호출해 크기를 바꿀 수 없습니다.
4.5 DSA는 glNamedBufferData 등으로 객체를 직접 지정합니다. 범위·접근·동기화 계약은 그대로입니다. 공유 버퍼를 여러 VAO가 참조하는 경우 한 번의 갱신이 모든 해당 draw에 영향을 줍니다. 사용 종료 뒤 삭제하되, CPU의 마지막 포인터 사용도 먼저 끝내야 합니다.
참고 링크
4 sources