유니티 렌더링 최적화하기 1 – 번역
유니티 그래픽스 렌더링 최적화하기 1
(Optimizing graphics rendering in Unity games)
– 원문 링크 –
개요 (Introduction)
이 글을 통해서, 유니티가 프레임 마다 렌더링을 처리할때 내부에서 일어나는 일과 렌더링 과정에서 발생할 수 있는 성능 문제(Performance problem), 그리고 렌더링과 관련된 성능 문제를 해결하는 방법에 대해서 살펴볼 예정입니다.
이 글을 읽기 전에, 렌더링 성능을 향상시키기위한 접근 방식 중에서, 모든 문제를 해결할 수 있는 단일 해결책은 없다는 점을 이해하는 것이 중요합니다. 렌더링 성능은 게임 내의 많은 요소에 의해서 영향을 받습니다. 또한 게임이 실행되는 하드웨어 및 운영체제에 의해서도 크게 영향을 받습니다.
기억해야할 것은, 문제를 조사하고, 실험하고, 엄격한 프로파일링을 통해서 문제를 해결한다는 것입니다.
이 글은, 가장 일반적인 렌더링 성능 문제에 대한 정보와 이런 문제를 해결하는 방법, 그리고 추가로 읽으면 좋은 글의 링크가 포함되어 있습니다. 물론, 여러분의 게임에서 겪고 있는 문제를 이 글에서 모두 다루지 못할수도 있습니다. 그러나, 이 글은 적어도 문제를 이해하고 효율적으로 해결책을 찾는데 필요한 기반 지식 및 용어를 익히는데 도움이 될 것입니다.
렌더링에 대한 간략한 소개 (A brief introduction to rendering)
시작하기에 앞서, 유니티가 한 프레임을 렌더링할때 벌어지는 과정들을 빠르고 간단하게 살펴보겠습니다. 이벤트(Event)의 흐름과 용어의 올바른 이해는, 앞으로 성능 문제를 이해하고 해결하기 위해서 실험하고 연구하는데 많은 도움이 될것입니다.
주의: 이 글에서, “오브젝트”라는 용어는 게임에서 렌더링되는 물체를 지칭하는데 사용합니다.
렌더러(Renderer) 컴포넌트를 갖는 게임오브젝트는 모두 오브젝트로 간주합니다.
기본적으로, 렌더링은 다음과 같이 설명할 수 있습니다:
- CPU라고 알려진, 중앙 처리 장치는 화면에 그려야하는 대상과 그 대상을 그리는 방법을 결정합니다.
- CPU는 GPU라고 알려진, 그래픽 처리 장치로 명령을 전달합니다.
- GPU는 CPU의 지시에 따라서 화면을 그립니다.
이제 좀 더 자세히 살펴보겠습니다.
렌더링 과정의 각 단계는 이 글의 뒷부분에서 자세히 다룰 예정이지만, 지금은 사용되는 단어와 익숙해지고, 렌더링에서 CPU 와 GPU의 역할이 어떻게 다른지에 대해서 이해해 보겠습니다.
렌더링을 설명하는데 종종 사용되는 문구가 바로 렌더링 파이프라인(Rendering Pipeline)입니다. 이 문구를 숙지하고 있으면 좋습니다.
효율적인 렌더링은 모두 정보 흐름을 유지하는 것입니다.
매 프레임에 렌더링이 될때마다, CPU는 다음과 같은 작업을 처리합니다:
- CPU는 오브젝트의 렌더링 여부를 결정하기 위해서, 씬(scene)안의 모든 오브젝트를 검사합니다. 특정 기준을 충족하는 오브젝트만 렌더링됩니다. 예를 들어, 오브젝트가 렌더링되기 위해서는 카메라의 뷰 프러스텀(Frustum) 내에 위치해야 합니다. 렌더링되지 않는 오브젝트는 cull 처리 된다고 말합니다. 프러스텀 및 프러스텀 컬링(Frustum Culling)에 대한 자세한 정보는 이 페이지를 참고하시기 바랍니다.
- CPU는 렌더링 되는 모든 오브젝트에 대한 정보를 수집하고, 드로우 콜(Draw Call)이라는 명령 단위로 정렬합니다. 드로우 콜에는 단일 메쉬와 이 메쉬가 렌더링되는 방법에 대한 정보가 담겨있습니다; 예를 들어, 어떤 텍스쳐를 사용해야하는지 등에 대한 정보가 담겨있습니다. 특정 상황에서 설정을 공유하는 오브젝트들은 동일한 드로우 콜로 결합될 수 있습니다. 서로 다른 오브젝트를 동일한 드로우 콜로 결합하는 것을 배칭(Baching)이라고 합니다.
- CPU는 매 드로우 콜(Draw call)마다, 배치(Batch)라고 불리는 데이터 패킷을 생성합니다. 배치(batch)에는 드로우 콜 외에 다른 데이터가 포함되는 경우도 있지만, 일반적으로 이런 상황은 성능 문제에 영향을 미칠 가능성이 거의 없기때문에, 이 글에서는 이를 고려하지 않겠습니다.
드로우 콜을 포함하는 모든 배치 마다, CPU는 다음의 과정을 처리하게 됩니다:
- CPU는, 렌더 상태(render state)라고 알려진 여러 변수의 값(숫자, number)을 변경하기 위해서 GPU에 명령을 전달합니다. 이 명령을 SetPass Call 이라고 합니다. SetPass Call은 다음번 메쉬를 렌더링하는데 사용할 설정을 GPU에 알려주는 역할을 합니다. SetPass Call은, 다음번에 렌더링 예정인 메쉬에서, 이전에 렌더링된 메쉬의 상태를 변경해야하는 경우에만 전달됩니다.
- CPU는 GPU에 드로우 콜을 전달합니다. 드로우 콜은 가장 최근에 전달된 SetPass Call에 정의된 설정들을 사용해서, 지정된 메쉬를 렌더링하도록 GPU에 지시합니다.
- 특정 상황에서는, 배치(batch)에 두개 이상의 패스(pass)가 필요한 경우도 있습니다. 패스(pass)는 쉐이더 코드의 섹션(section)이며, 패스가 추가될 때마다 렌더 상태의 변경이 발생합니다. 배치의 각 패스마다, CPU는 새로운 SetPass Call을 전달한 다음, 드로우 콜을 다시 전달해야합니다.
반면에, GPU는 다음과 같은 작업을 처리합니다:
- GPU는 CPU에서 전달받은 순서대로 작업을 처리합니다.
- 현재 처리하는 작업이 SetPass Call인 경우, GPU는 렌더 상태(render state)를 업데이트합니다.
- 현재 처리하는 작업이 드로우 콜(Draw Call)인 경우, GPU는 메쉬를 렌더링합니다. 이 과정은 쉐이더 코드의 개별 섹션(section)에 정의된 단계에서 발생합니다. 렌더링 과정에서 이 부분은 매우 복잡하기 때문에, 이 글에서는 아주 자세히 다루지는 않습니다. 하지만, 버텍스 쉐이더(vertex shader)라 불리는 코드 섹션은, GPU에 메쉬의 정점(vertices)을 처리하는 방법을 전달하고, 프래그먼트 쉐이더(fragment shader)라 불리는 코드 섹션은, GPU에 개별 픽셀(pixel)을 그리는 방법을 전달한다는 점을 이해하는 것은 도움이 됩니다.
- 이 프로세스는 CPU로부터 전달된 작업이 GPU에서 모두 처리될때까지 반복됩니다.
지금까지 유니티가 프레임을 렌더링할때 벌어지는 과정에 대해서 살펴봤습니다. 이제, 렌더링 과정에서 발생할 수 있는 문제에 대해서 살펴보겠습니다.
내용 끝까지 읽어주셔서 감사합니다.
배너 클릭은 저에게 많은 힘이 됩니다.
감사합니다 🙂