사전 스토어 연산
개념

스토어 연산

gabury1고친 사람 github-actions[bot]

스토어 연산은 그리기를 마칠 때 다 그린 그림을 남길지 버릴지 정합니다. 남기면 그림이 메모리에 써집니다. 버리면 써 내는 일을 건너뜁니다. 그래픽 카드는 이 답을 보고 쓸모없는 쓰기를 줄입니다. 컴퓨터 구조에서는 프로세서가 값을 메모리에 써 넣는 명령도 같은 이름으로 부릅니다.

쉽고 빠른 이해

그리기 묶음을 닫으면서 「방금 그린 이 이미지를 나중에 또 쓰나」를 알리는 설정입니다. 화면에 띄울 그림은 남겨야 합니다. 앞뒤를 가리는 데만 쓴 보조 이미지는 버려도 됩니다.

알리지 않으면 그래픽 카드는 모든 이미지를 남겨 둡니다. 다시 읽지 않을 이미지를 초마다 수십 번 메모리에 써 내게 됩니다.

  1. 화면에 띄우거나 다음 단계가 읽을 이미지는 「남기기」를 고릅니다
  2. 그리는 동안만 쓰고 끝나면 볼 일 없는 이미지는 「버리기」를 고릅니다
  3. 다음 묶음은 남긴 이미지를 불러와 이어 그릴 수 있습니다

대가는 고른 값이 틀리면 화면이 깨진다는 점입니다. 나중에 쓸 이미지를 「버리기」로 닫으면 다음에 읽을 때 알 수 없는 값이 나옵니다.

상세

그리기를 마칠 때 내리는 결정

이 소절은 그리기 명령이 그래픽 카드에 들어가는 길을 따라가며 스토어 연산을 언제 고르는지 보입니다. 먼저 회의실 화이트보드에 견줘 봅니다.

회의가 끝나면 화이트보드에 판서가 가득합니다. 다음 회의에서 이어 볼 내용이면 사진을 찍어 둡니다. 계산을 하느라 끄적인 낙서뿐이면 사진 없이 지우고 나갑니다. 사진을 찍는 데는 시간이 드니, 남길 것만 찍어야 시간이 덜 듭니다.

그래픽 카드에서 그리기를 맡는 칩이 GPU(Graphics Processing Unit, 그래픽 처리 장치)입니다. 화면에 그릴 점이 수백만 개라 이 일만 따로 맡는 칩을 둡니다.

프로그램은 그래픽스 API(Application Programming Interface, 응용 프로그래밍 인터페이스)에 그리기 명령을 적어 GPU 에 넘깁니다. 그래픽스 API 는 프로그램이 GPU 에 일을 시키는 창구입니다.

많은 그래픽스 API 는 그리기 명령을 렌더 패스라는 묶음으로 받습니다. 묶음을 열 때 어느 이미지에 그릴지 알립니다. 닫을 때까지 들어온 명령은 전부 그 이미지에 그립니다. 묶음 단위로 받으면 GPU 가 메모리를 오가는 순서를 미리 짤 수 있습니다.

렌더 패스에 걸어 둔 이미지를 어태치먼트라고 부릅니다. 패스에 붙여 둔 이미지라는 뜻입니다. 한 패스에 여러 장을 걸 수 있어서, 패스를 닫을 때도 이미지마다 따로 처리합니다.

이미지는 화면의 점 하나(픽셀)마다 값을 하나씩 담습니다. 이 문서는 그 점 하나를 칸이라고 부릅니다.

가장 흔한 어태치먼트는 렌더 타깃입니다. 화면에 보일 색을 받는 이미지라 칸마다 색이 들어갑니다.

그다음으로 흔한 것은 깊이 버퍼입니다. 칸마다 카메라에서 떨어진 거리가 들어갑니다. 물체가 겹치면 이 거리를 견줘 앞에 있는 것만 그립니다.

스토어 연산은 렌더 패스를 닫을 때 어태치먼트마다 고르는 값입니다. 패스 동안 그린 결과를 메모리에 남길지 정합니다. 회의실에 견주면 사진을 찍고 나가는 쪽이 남기기, 사진 없이 지우고 나가는 쪽이 버리기입니다.

패스를 열 때도 짝이 되는 값을 고릅니다. 이미지에 이미 있던 내용을 불러올지(load), 정해 둔 색으로 지울지(clear), 신경 쓰지 않을지(don't care) 정하는 값이 로드 연산입니다. 로드 연산은 시작을, 스토어 연산은 끝을 맡습니다.

고를 수 있는 두 값

값은 대개 둘입니다. 이름은 그래픽스 API 마다 조금씩 다르지만 뜻은 같습니다. 쉽고 빠른 이해의 「남기기」가 store, 「버리기」가 don't care 입니다.

값 패스를 닫을 때 하는 일 고르는 때
store 그린 결과를 메모리에 써서 남긴다 화면에 띄우거나 뒤에서 다시 읽을 때
don't care 그린 결과를 버려도 된다고 알린다 패스가 끝나면 볼 일이 없을 때

store 는 가장 안전한 값입니다. 결과가 남으니 다음 패스가 로드 연산을 load 로 두고 이어 그릴 수 있습니다. 화면에 내보내는 이미지도 store 로 닫아야 화면에 제대로 뜹니다.

don't care 는 「이 결과는 앞으로 아무도 안 읽는다」는 약속입니다. 그래서 패스를 닫은 뒤 그 이미지의 칸마다 무엇이 들어 있을지 알 수 없습니다. 방금 그린 그림이 남아 있을 수도 있고 뜻 없는 값이 있을 수도 있습니다.

값은 어태치먼트마다 따로 고릅니다. 한 패스 안에서 렌더 타깃은 store 로 닫고 깊이 버퍼는 don't care 로 닫을 수 있습니다. 이 조합이 가장 흔합니다.

쓰기를 아끼는 원리

스토어 연산을 미리 알리면 왜 빨라지는지는 GPU 의 메모리 구조에서 나옵니다. 이 소절은 메모리 두 종류와 그 사이를 오가는 화살표 하나로 그 이유를 보입니다.

GPU 가 쓰는 이미지는 주 메모리에 있습니다. 주 메모리는 크지만 칩 바깥에 있어 오가는 데 시간과 전력이 듭니다. 칩 안에도 작은 메모리가 있습니다. 작지만 금방 닿습니다.

타일 기반 렌더링을 쓰는 GPU 는 화면을 작은 사각형 조각으로 나눠 그립니다. 이 조각이 타일입니다. 타일 하나를 칩 안 메모리에 올려 둡니다. 거기 걸리는 그리기를 다 마친 뒤 다음 타일로 넘어갑니다. 휴대폰의 GPU 가 대개 이 방식입니다.

이런 GPU 에서 두 값은 다 그린 타일을 어떻게 내려놓을지 가릅니다. store 는 타일을 주 메모리의 이미지에 써 냅니다. don't care 는 써 내지 않고 칩 안의 타일을 그냥 비웁니다.

flowchart TD
    subgraph CHIP["칩 안 메모리 · 작고 가깝다"]
        T1["store · 다 그린 렌더 타깃 타일"]
        T2["don't care · 다 그린 깊이 버퍼 타일"]
    end
    subgraph MAIN["주 메모리 · 크고 멀다"]
        IMG1["렌더 타깃 이미지"]
        IMG2["깊이 버퍼 이미지"]
    end
    T1 -->|"써 낸다"| IMG1

그림에서 주 메모리로 가는 화살표는 store 하나뿐입니다. 깊이 버퍼 이미지도 주 메모리에 있지만, don't care 로 닫은 깊이 버퍼 타일은 칩 안에서 끝납니다. 타일마다 아낀 쓰기가 모이면 이미지 한 장을 안 쓰는 셈이 됩니다.

양을 어림해 보면 이렇습니다. 가로 1920 · 세로 1080 칸에 칸마다 4바이트를 쓰는 깊이 버퍼는 한 장이 약 8MB 입니다. 초에 60번 그리면서 매번 store 로 닫으면 초마다 약 500MB 를 써 냅니다. 다시 읽지 않을 깊이라면 이 쓰기는 전부 버리는 일입니다.

스토어 연산을 알릴 수 없는 경우도 있습니다. 렌더 패스가 없는 옛 그래픽스 API 에는 이 값을 적을 곳이 없습니다. 쉽고 빠른 이해에서 말한 「알리지 않으면」이 이 경우입니다.

드라이버는 그래픽스 API 의 명령을 GPU 가 알아듣는 일로 바꿔 주는 소프트웨어입니다. 옛 API 에서는 무엇을 써 낼지 이 드라이버가 짐작해야 합니다. 나중에 누가 읽을지 모르니 대개 모든 이미지를 써 냅니다.

화면을 조각내지 않는 GPU 에서는 차이가 작은 편입니다. 이런 GPU 는 처음부터 주 메모리의 이미지에 바로 그려서 타일을 내려놓는 단계가 없습니다. 데스크톱용 GPU 가 대개 그렇습니다.

값을 고르는 기준

묻는 것은 하나입니다. 이 패스가 끝난 뒤 이 이미지를 읽는 곳이 있는지입니다. 있으면 store, 없으면 don't care 입니다.

읽는 곳은 여러 모양으로 나타납니다. 화면에 띄우는 것도 읽기입니다. 다음 패스가 load 로 여는 것도 읽기입니다. 다음 패스가 그 이미지를 텍스처로 읽는 것도 읽기입니다. 텍스처는 그릴 때 값을 꺼내 쓰는 이미지입니다.

그림자 매핑이 텍스처로 읽는 예입니다. 먼저 빛이 있는 곳에서 본 깊이를 한 패스로 그려 둡니다. 다음 패스는 그 깊이를 텍스처로 읽어 어느 점이 그림자 안에 드는지 가립니다. 그래서 이때의 깊이 버퍼는 store 로 닫습니다.

이런 경우가 아니면 깊이 버퍼는 대개 don't care 입니다. 깊이 값은 그리는 동안 앞뒤를 가리는 데만 씁니다. 장면을 다 그리고 나면 다시 볼 일이 없을 때가 많습니다.

스텐실 버퍼도 비슷합니다. 칸마다 작은 정수를 담아 어느 칸에 그릴지 거르는 보조 이미지입니다. 이것도 그리는 동안만 쓰는 일이 많아 don't care 로 닫곤 합니다.

흔한 실수는 두 갈래입니다. 다시 안 읽을 이미지를 store 로 닫으면 쓰지도 않을 내용을 써 냅니다. 결과는 맞지만 느립니다.

반대로 뒤에서 읽을 이미지를 don't care 로 닫으면 다음에 읽을 때 알 수 없는 값이 나옵니다. 어떤 GPU 에서는 방금 그린 그림이 남아 멀쩡해 보입니다. 다른 GPU 에서는 같은 프로그램이 깨진 조각을 보입니다. 한 기계에서 시험해서는 잡히지 않는 버그가 됩니다.

멀티샘플 이미지를 합쳐 내보내기

이 소절은 한 패스에 걸린 이미지 두 장에 서로 다른 스토어 연산을 고르는 예를 봅니다. 큰 이미지는 버립니다. 그 이미지를 합친 작은 이미지만 남깁니다.

계단처럼 깨진 가장자리를 부드럽게 하려고 칸 하나에 값을 여럿 담는 방법이 있습니다. 이 방법이 멀티샘플 안티에일리어싱입니다. 값을 여럿 담은 이미지는 멀티샘플 이미지라고 부릅니다. 칸마다 값이 넷이면 이미지도 네 배로 큽니다.

화면에 띄우려면 칸마다 값 하나로 줄여야 합니다. 여러 값을 평균 내어 보통 이미지 한 장에 담는 일이 멀티샘플 리졸브입니다. 이 합치기는 패스를 닫을 때 일어납니다.

합친 이미지는 store 로 남깁니다. 큰 멀티샘플 이미지는 합친 뒤에는 볼 일이 없어 don't care 로 닫습니다. 타일 기반 GPU 는 이 합치기를 칩 안에서 할 수 있습니다. 그러면 네 배짜리 이미지는 주 메모리에 한 번도 써지지 않습니다. 작은 결과 한 장만 써집니다.

두 패스로 본 스토어 연산

아래는 나무를 그리고 그 위에 점수를 덧그리는 두 패스입니다. 어느 그래픽스 API 의 문법도 아니고 흐름만 보인 코드입니다. 나무는 초록, 점수는 흰색으로 그립니다. 오른쪽 주석은 점수가 닿지 않는 나무 칸 하나에 그 줄을 지난 뒤 담긴 색입니다.

begin_pass(화면, clear(검정)) // 검정
draw(나무, 초록)              // 초록
end_pass(화면, store)         // 초록
begin_pass(화면, load)        // 초록
draw(점수, 흰색)              // 초록
end_pass(화면, dont_care)     // 알 수 없음

첫 패스는 store 로 닫아 초록이 주 메모리에 남습니다. 그래서 둘째 패스가 load 로 열었을 때 초록을 다시 불러옵니다.

둘째 패스는 don't care 로 닫았습니다. 점수는 그렸지만 결과를 남기지 않았으니 화면에 무엇이 뜰지 알 수 없습니다. 마지막 줄을 store 로 바꾸면 나무 칸은 초록, 점수 칸은 흰색으로 뜹니다.

같은 이름의 CPU 명령

CPU(Central Processing Unit, 중앙 처리 장치) 쪽에서 스토어 연산은 전혀 다른 일을 가리킵니다. 레지스터에 있는 값을 메모리에 써 넣는 명령입니다. 레지스터는 CPU 안에 있는 아주 작은 저장 칸입니다. 계산은 여기 올라온 값으로만 합니다.

반대 방향으로 메모리 값을 레지스터로 읽어 오는 명령은 로드라고 부릅니다. 메모리는 로드와 스토어로만 만지고 계산은 레지스터끼리만 하게 짠 설계를 로드-스토어 아키텍처라고 부릅니다. 컴퓨터 구조 교재에서 「스토어 연산」을 만나면 대개 이 뜻입니다.

관련 항목

스토어 연산과 짝을 이루는 패스 설정

로드 연산 · 클리어 값 · 멀티샘플 리졸브 · 렌더 패스 · 서브패스 · 다이내믹 렌더링

스토어 연산을 고르는 대상인 이미지

어태치먼트 · 렌더 타깃 · 깊이 버퍼 · 스텐실 버퍼 · 색 버퍼 · 프레임버퍼 · 이미지 뷰 · 임시 어태치먼트

스토어 연산이 비용을 줄이는 GPU 구조

GPU · 타일 기반 렌더링 · 타일 메모리 · 즉시 모드 렌더링 · 메모리 대역폭 · 디바이스 드라이버

스토어 연산을 잘못 골라 생기는 문제

미정의 동작 · 렌더 패스 데이터 레이스 · 피드백 루프

스토어 연산을 패스 설정으로 받는 그래픽스 API

Vulkan · Metal · WebGPU · Direct3D 12 · 그래픽스 API

스토어 연산으로 결과를 넘기는 그리기 단계

후처리 · 그림자 매핑 · 지연 셰이딩 · 멀티패스 렌더링 · 렌더 그래프 · 멀티샘플 안티에일리어싱

CPU 에서 같은 이름으로 부르는 명령

로드-스토어 아키텍처 · 레지스터 · 명령어 집합 · RISC · 메모리 접근 · 스토어 버퍼

스토어 연산이 속하는 상위 분류

그래픽스 · 렌더링 · 실시간 렌더링 · 그래픽스 파이프라인

다른 이름: store operation · store op · storeOp · store action · 스토어 오퍼레이션