3D Rendering

Drawcall

Keisa 2023. 9. 29. 16:48

출처: https://tsyang.tistory.com/79

 

드로우콜(Draw Call)

드로우콜 CPU는 현재 프레임에 어떤 것을 그려야 할지 정하고, GPU에 오브젝트를 그리라고 명령을 호출하는데 이 명령이 바로 드로우 콜(Draw Call)이다. 게임의 오브젝트를 화면에 렌더링하려면 우

tsyang.tistory.com

 

CPU가 GPU에게 물체의 렌더링을 요청하는 행위. 

 

CPU와  GPU는 병렬적으로 동작하고 CPU가 GPU에 명령할 것이 생긴다면 Command Buffer에 명령을 넣어준 뒤 작업을 이어나가고 GPU는 Command Buffe를 확인하여 명령을 수행한다. CPU가 명령을 내릴 때 작업을 멈추고 지시 후 복귀하는 등의 과정이 없이 둘은 병렬적으로 지속적으로 동작을 수행하고 있다는 점이 중요.

 

Drawcall은 전달되는 오브젝트의 숫자에 비례하여 드로우콜이 생성되며 디바이스의 종류와 사양에 따라 프레임당 처리할 수 있는 드로우콜의 수가 다르기 때문에 디바이스에 맞춰 드로우콜의 수를 적절하게 관리해야 한다.

 

● Drawcall에 영향을 주는 요소

메시, 머티리얼, 셰이더, 트랜스폼, 조명, 알파블렌딩 이중에서 머티리얼과 메시의 개수가 드로우콜의 증가에 가장 많은 영향을 준다.

Ex) 5개의 개별 액터가 존재한다고 한다면 각각의 개별 액터들이 1개씩 드로우콜을 생성하여 총 5개의 드로우콜이 나온다. 이때 5개의 액터를 메시를 병합하여 하나의 액터로 만들경우 1개의 드로우콜만 생성한다. 이는 머티리얼도 마찬가지다.

드로우콜 최적화 : 컬링으로 불필요한 오브젝트 제거, 여러 메시를 하나로 병합, 텍스처 시트로 머티리얼 수 감소

때문에 드로우콜을 최적화하기 위해서는 여러가지 컬링 기법들과 함께 텍스처와 메시의 병합을 적절하게 활용하여 한번에 렌더링 되는 고유한 오브젝트의 개수를 줄여야 한다.

 

LOD를 통해 오브젝트의 복잡도를 낮추고 Culling을 통해 그려지지 않을 물체를 제거하여 최대한 그려야할 물체들을 선별하여 그릴 수 있도록 해야한다.

 

Draw Call은 그래픽 파이프라인에서 GPU에게 그리기 명령을 전달하는 작업입니다. 즉, CPU가 GPU에 "이 오브젝트를 그려라"라는 명령을 내리는 개별적인 호출입니다. 여기서 그린다는 것은 **GPU의 렌더 타겟(Render Target)**에 데이터를 그리는 작업을 의미합니다. 렌더 타겟은 결국 프레임 버퍼(Framebuffer)에 저장되며, 최종적으로 백버퍼(Back Buffer)와 프론트버퍼(Front Buffer)가 교환되어 화면에 출력됩니다.

Draw Call의 대상

  1. 렌더 타겟(Render Target)
    • GPU가 직접 데이터를 그리는 버퍼입니다.
    • 렌더 타겟은 컬러 버퍼, 깊이(Depth) 버퍼, 스텐실 버퍼 등을 포함합니다.
    • 렌더 타겟이란 중간 결과를 저장하는 임시 버퍼로, 백버퍼에 그리기 전에 오브젝트별 렌더링이 이루어집니다.
  2. 백버퍼(Back Buffer)
    • 최종적으로 화면에 출력될 이미지의 중간 버퍼입니다.
    • GPU가 렌더 타겟에 데이터를 그린 후, 결과물을 백버퍼에 합성하거나 복사합니다.
  3. 프론트버퍼(Front Buffer)
    • 백버퍼의 데이터가 교환되어 화면에 표시되는 버퍼입니다.

렌더링 과정 요약

  1. CPU는 Draw Call을 통해 GPU에 "메시를 그려라"라는 명령을 보냅니다.
  2. GPU는 파이프라인을 통해 정점 쉐이더(Vertex Shader) → 픽셀 쉐이더(Pixel Shader)를 거쳐 **렌더 타겟(Render Target)**에 데이터를 씁니다.
  3. 모든 렌더링이 완료되면, 결과물이 백버퍼에 합성됩니다.
  4. **백버퍼와 프론트버퍼를 스왑(Swap)**하여 최종적으로 화면에 표시됩니다.

Draw Call과 최종 출력의 관계

  • Draw CallGPU 렌더 타겟에 그리는 명령입니다.
  • 최종적으로 여러 개의 렌더 타겟과 Draw Call을 거쳐 만들어진 결과물이 백버퍼에 저장되고, 그 백버퍼가 화면으로 교환(Swap Chain)됩니다.

핵심 포인트

Draw Call 자체는 GPU 명령이고, 그리기 작업의 "단위"를 의미하며 렌더 타겟에서 실행됩니다. 최종 결과는 백버퍼에 모이게 되며, 화면 출력 시 백버퍼와 프론트버퍼가 교체됩니다.

 

CPU는 현재 프레임에 어떤 것을 그려야 할지 정하고, GPU에 오브젝트를 그리라고 명령을 호출하는데 이 명령이 바로 드로우 콜(Draw Call)이다. 

 

게임의 오브젝트를 화면에 렌더링하려면 우선 오브젝트가 렌더링 대상인지를 판단한다. 이러한 과정을 컬링이라고 한다. 컬링을 거친 오브젝트가 렌더링 되기 위해선 CPU에서 GPU에 다음의 정보를 줘야 한다.

  • 메시 정보
  • 텍스처 정보
  • 쉐이더 정보
  • 트랜스폼 정보
  • 알파 블렌딩 여부
  • 기타 등등

 

 

 

메시 텍스쳐 쉐이더등의 정보는 스토리지에 보관되어 있다가 CPU가 이를 읽어들여 CPU 메모리에 데이터를 올린다. 그 후 CPU메모리에 있는 정보들을 GPU 메모리로 복사한다. 정보들은 GPU 메모리에 있어야 GPU가 사용할 수 있다.

 

만약 위 복사과정이 매 프레임마다 일어난다면 성능을 많이 잡아먹을 것이다. 따라서 로딩 시점에 데이터를 미리 메모리에 올려둔다. 

 

오브젝트를 렌더링하기 시작하면 GPU에 어떤 텍스처를 사용 할지, 어떤 버텍스들을 사용 할지, 어떤 쉐이더를 사용 할지등을 순차적으로 알려줘야 한다. 이런 정보들은 GPU의 상태 정보를 담는 테이블에 저장된다. 이 테이블을 렌더 상태(Render States)라고 부르며 각각의 요소는 GPU 메모리를 가리키는 포인터를 저장한다. 

 

Render States에는 알파 블렌딩 여부, Z테스트 여부, 기타 등등의 정보도 포함됨

CPU가 렌더 상태를 변경하는 명령을 GPU에 보내고 나면 CPU는 마지막으로 GPU에 메시를 그리라는 명령을 보낸다. 이 명령을 Draw Primitive Call(DP Call)이라고 부른다. GPU는 DP Call을 받으면 렌더 상태의 정보들을 마탕으로 오브젝트의 메시를 렌더링한다.

 

한 오브젝트의 메시가 렌더링 됐다면 CPU는 또 다른 오브젝트를 렌더링 하기 위해 사용할 쉐이더, 메시, 텍스쳐등의 정보들을 변경하는 명령을 한다. 그렇게 되면 Render States역시 바뀔 것이다. 그 후 DP Call을 받은 GPU가 다시 오브젝트를 렌더링한다. 

 

이처럼 한 오브젝트를 그릴 때마다 CPU가 매번 렌더 상태 정보들을 변경하라는 명령을 한 뒤, DP Call을 해준다. 이런 과정을 넓은 의미에서 드로우 콜이라고 한다.

 

CPU가 GPU에 명령을 보낼 때 명령들을 잠시 저장하는 버퍼가 존재하는데 이를 커맨드 버퍼라고 부른다. 커맨드 버퍼가 존재함으로써 CPU와 GPU는 서로 비동기적으로 일을 처리할 수 있다. 이런 커맨드 버퍼는 그래픽스 API마다 구현 방식이 다르며 여러 개의 커맨드 버퍼나 여러 개의 쓰레드를 사용하기도 한다.

 

문제는 드로우 콜에서 사용되는 명령들이 모두 GPU가 알아들을 수 있는 명령들로 변환되어야 하는데 이것이 CPU 오버헤드를 발생시키며 따라서 드로우 콜은 대게 CPU 병목의 주 원인이다.

 

드로우 콜로 인한 병목을 줄이기 위해선 드로우 콜 호출 횟수 자체를 줄여야 한다.

 

 


드로우 콜의 발생 조건

 

기본적으로 오브젝트를 그릴 때 메시가 1개, 머테리얼이 1개라면 드로우콜이 한 번 일어난다.

 

메시가 여러 개이면 드로우 콜도 여러 번 일어난다. 예를 들어, 한 오브젝트의 메시가 10개라면 해당 오브젝트를 렌더링 하는데 드로우 콜이 10번 발생한다. 만약 10개의 메시로 이뤄진 오브젝트가 20개 있다면 드로우 콜은 200번 발생하게 된다. 따라서 오브젝트의 파츠는 적을 수록 좋다.

 

마찬가지로 메시가 1개이지만 머티리얼이 여러 개인 경우에도 여러 번의 드로우콜이 발생한다. (서브 메시 생성)

 

쉐이더 내에서 멀티패스(Multi Pass)로 두 번 이상 렌더링을 하는 경우도 드로우 콜이 여러 번 발생한다. (ex. 카툰 렌더링 쉐이더에서 추가적으로 외곽선을 그려주는 경우)

 


 

Batch & Set Pass

유니티에서는 Batch와 Set Pass를 구분하는데, Batch는 넓은 의미의 드로우 콜이고 Set Pass는 쉐이더로 인한 렌더링 패스 횟수이다. 오브젝트를 렌더링 하는 중 머티리얼이 바뀌면 쉐이더 및 파라미터들이 바뀌면서 SetPass가 증가한다. 이 때 많은 상태 변경들이 일어나기 때문에 SetPass도 CPU 성능을 꽤 잡아먹는다.

 

예를 들어, 10개의 오브젝트가 같은 메테리얼을 사용한다면 Set Pass 횟수는 1이다. 그러나 10개의 오브젝트가 각기 다른 메테리얼을 사용한다면 Set Pass 횟수는 10이 된다. 그러나 서로 다른 메시를 사용하더라도 모두 동일한 머티리얼을 사용한다면 SetPass는 한 번만 발생한다.