유니티 FPS가 떨어지는 이유 (CPU Bound vs GPU Bound 완벽 정리)

유니티 FPS 떨어지는 이유는 무엇일까요?

유니티를 활용해 프로젝트를 진행하다 보면 최적화 관련된 고민을 하는 순간이 올 때가 있습니다.

“갑자기 FPS가 60에서 30으로 떨어졌다”
“최적화를 한다고 했는데도 성능이 좋아지지 않는다”
“Draw Call을 줄였는데 왜 그대로지?”

이러한 문제들의 공통점은 하나입니다.

“문제를 정확히 모르고 최적화를 시도했다”는 점입니다.

렌더링 최적화는 감으로 하는 작업이 아닙니다.
감이나 경험을 떠올리기보다 먼저 해야 할 일이 있습니다.

👉 지금 프로젝트가 CPU Bound인지, GPU Bound인지 구분하는 것

이 글에서는 유니티 렌더링 최적화의 가장 기본이 되는 이 개념을 정리합니다.


//
//

렌더링 최적화의 출발점

많은 분들이 최적화를 시작할 때 이렇게 접근합니다.

“일단 Draw Call 줄이자”
“Batching 적용하자”
“텍스처 줄이자”
“등등 …”

성능을 발목 잡는 원인 중 렌더링 관련 문제가 큰 비중을 차지하기 때문에 이러한 접근이 타당해 보이기도 합니다.
또한, 예전에 이런 방식으로 최적화를 적용했더니 성능이 올라갔던 경험이 있었을 수도 있습니다.

하지만 이렇게 방법을 앞세워 무작정 최적화 기법을 적용하면 실패하는 경우가 많습니다.

왜냐하면 병목 지점을 모른 채 최적화를 시도하고 있기 때문입니다.

렌더링은 PC에서 핵심인 CPU와 GPU 두 프로세싱 유닛이 협업을 해서 이루어내는 예술과 같습니다.
즉, 렌더링 성능은 이 두 프로세싱 유닛에 의해 결정됩니다.

CPU: 화면에 무엇을 그릴지를 준비합니다.
GPU: 실제로 화면에 그림을 그립니다.

CPU가 GPU에 어떤 것을 그릴지 전달하지 않으면, GPU는 CPU가 일을 주기 전까지 기다릴 수 밖에 없습니다.
반대로 CPU가 GPU에 아무리 그릴 것을 전달하더라도 GPU가 무언가를 그리느라 너무 바쁘면 CPU가 전달한 일은 처리되지 않고 쌓일 수 밖에 없습니다.

따라서 CPU와 GPU 중에서 어느 쪽이 성능의 발목을 잡고 있는지를 명확하게 확인하는 것이 중요합니다.

어느 쪽이 느린지에 따라 해결 방법이 완전히 달라지기 때문입니다.


CPU Bound와 GPU Bound

유니티 CPU Bound와 GPU Bound 차이를 설명하는 렌더링 성능 병목 구조 다이어그램
CPU가 병목이면 GPU는 대기 상태가 되고, GPU가 병목이면 CPU가 준비한 작업이 쌓이게 됩니다.

👉 이 개념을 이해하지 못하면 최적화 방향을 잘못 잡을 가능성이 높습니다.

 

이제 본격적으로 핵심 개념을 정리해보겠습니다.

앞서 설명했듯이 렌더링은 CPU와 GPU가 협업해서 이루어집니다.
그리고 이 두 유닛 중 어느 한쪽이라도 느려지면 전체 성능은 그쪽에 맞춰 떨어지게 됩니다.

이 상황을 구분하기 위해 사용하는 개념이 바로 CPU BoundGPU Bound입니다.

CPU Bound

CPU Bound는 CPU가 병목이 되는 상황을 의미합니다.

조금 더 쉽게 풀어서 보면,
GPU는 충분히 빠르게 작업을 처리할 수 있는 상태인데 CPU가 GPU에게 일을 제때 넘겨주지 못하고 있는 상황입니다.

즉, GPU는 놀고 있고 CPU가 일을 못해서 전체 프레임이 떨어지는 상태입니다.

이러한 상황은 다음과 같은 원인에서 자주 발생합니다.

  • Draw Call이 과도하게 많은 경우
    CPU는 오브젝트 하나를 그릴 때마다 Draw Call을 생성해서 GPU에 전달해야 합니다.
    이 수가 많아질수록 CPU의 부담이 급격히 증가합니다.
  • Batching이 제대로 이루어지지 않는 경우
    원래 하나로 묶어서 처리할 수 있는 오브젝트들이 개별적으로 처리되면서 CPU 작업량이 증가합니다.
    Draw Call이 증가하기 때문입니다.
  • SetPass Call이 많은 경우
    머티리얼이나 쉐이더 상태 변경이 자주 발생하면 CPU가 GPU에 설정을 계속 다시 전달해야 합니다.
    이 또한 CPU 비용을 증가시키는 주요 원인입니다.
  • 씬에 오브젝트 수가 과도한 경우
    렌더링 대상이 많아질수록 CPU는 더 많은 준비 작업을 수행해야 합니다.

이러한 상황에서는 GPU가 처리할 준비는 되어 있지만,
CPU가 병목이 되어 GPU에게 충분한 작업을 전달하지 못하고 있는 상태입니다.

따라서 이 경우의 해결 방향은 자연스럽게 다음과 같이 정리됩니다.

👉 Draw Call 줄이기 / Batching 적용 / 렌더링 구조 개선

 

GPU Bound

GPU Bound는 GPU가 병목이 되는 상황을 의미합니다.

이 경우는 CPU가 충분히 빠르게 작업을 준비해서 GPU에게 넘겨주고 있지만,
GPU가 그 작업을 다 처리하지 못해서 프레임이 떨어지는 상황입니다.

즉, CPU는 이미 일을 다 해놓고 기다리고 있지만,
GPU가 너무 바빠서 전체 프레임이 늦어지고 있는 상태입니다.

이러한 상황은 다음과 같은 원인에서 자주 발생합니다.

  • Overdraw가 많은 경우
    같은 픽셀을 여러 번 그리는 상황이 많아질수록 GPU의 부담이 크게 증가합니다.
    특히 투명 오브젝트나 UI가 많은 경우 자주 발생합니다.
  • 해상도가 높은 경우
    해상도가 높아질수록 GPU는 더 많은 픽셀을 처리해야 하기 때문에 부담이 증가합니다.
  • Shader 비용이 큰 경우
    복잡한 쉐이더나 연산이 많은 머티리얼을 사용할수록 GPU의 처리 시간이 증가합니다.
  • 포스트 프로세싱 효과가 많은 경우
    Bloom, Motion Blur, Depth of Field 등의 효과는 GPU 비용을 크게 증가시킬 수 있습니다.

이러한 상황에서는 CPU가 아무리 빠르게 작업을 준비하더라도
GPU가 그 작업을 처리하지 못하면 전체 성능은 개선되지 않습니다.

따라서 이 경우의 해결 방향은 다음과 같이 정리됩니다.
👉 픽셀 처리량 줄이기 / 쉐이더 최적화 / 해상도 조절

이 두 가지 개념을 명확하게 구분하는 것이 중요한 이유는 단순합니다.
👉 병목이 어디에 있느냐에 따라 “해야 할 최적화”가 완전히 달라지기 때문입니다.


//

많이 하는 실수

이 개념을 이해하지 못하면 다음과 같은 실수를 하게 됩니다.

GPU Bound 상황인데 Draw Call을 줄이려고 하는 경우
👉 병목 지점은 GPU인데 CPU의 일을 덜어주는 방식이기 때문에 성능이 크게 올라가기 어렵습니다.

또는

CPU Bound 상황인데 텍스처 해상도를 낮추는 경우
👉 병목 지점은 CPU인데 GPU의 일을 덜어주는 방식이기 때문에 이 역시 성능 향상으로 이어지기 어렵습니다.

이러한 시도는 대부분 효과가 없거나, 미미한 개선만 가져옵니다.

그래서 최적화는 항상 이 순서를 따라야 합니다.

  1. 병목 확인
  2. 원인 분석
  3. 적절한 해결 방법 적용

이 중에서 1번과 2번이 특히 중요하다고 할 수 있습니다.

이 순서를 건너뛰는 순간, 최적화 과정은 소위 말하는 “노가다” 작업이 되기 쉽습니다.


유니티에서 병목 확인하는 방법

그렇다면 실제로 CPU Bound인지 GPU Bound인지 어떻게 확인할까요?

유니티는 성능 분석을 위해 다양한 도구를 제공하며, CPU와 GPU Bound를 확인할 때는 다음 도구를 사용합니다.

  • Profiler
  • Frame Debugger

이 중에서 가장 먼저 확인해야 할 것은 Profiler입니다.

Profiler에서 CPU와 GPU 시간을 비교하면 됩니다.

  • CPU 시간이 더 길다 → CPU Bound
  • GPU 시간이 더 길다 → GPU Bound

예를 들어 다음과 같은 경우를 생각해봅시다.

  • CPU: 25ms / GPU: 10ms → CPU Bound
  • CPU: 10ms / GPU: 30ms → GPU Bound

이렇게 병목 지점만 정확히 파악해도 최적화 방향이 완전히 달라집니다.


정리

렌더링 최적화의 핵심은 특정 기술이 아닙니다.

“어디가 느린지를 정확히 아는 것”입니다.

  • CPU가 느리면 → Draw Call / Batching
  • GPU가 느리면 → Overdraw / Shader

이 기준 없이 최적화를 진행하면
시간을 많이 쓰고도 원하는 결과를 얻지 못할 가능성이 높습니다.


다음 글에서 다룰 내용

다음 글에서는 많은 분들이 가장 궁금해하는 주제를 다룹니다.

👉 Draw Call은 왜 줄여야 하는가? (Batching과 SetPass Call의 진짜 의미)

이 개념을 이해하면 렌더링 최적화의 절반은 이해했다고 보셔도 됩니다.


🎯 한 단계 더 나아가고 싶다면

렌더링 구조를 제대로 이해하려면 단순히 사용하는 것을 넘어서 직접 구현해보는 경험이 큰 도움이 됩니다.

이에 더해 게임 엔진 내부에서 동작하는 여러 시스템을 직접 구현해보면 상용 엔진의 동작 원리를 보다 분명하게 이해할 수 있습니다.

게임 엔진의 동작 원리를 분명하게 이해하고 싶은 분들에게 아래 강의를 추천합니다.

👉C++로 만드는 게임 엔진 프레임워크 (소코반과 슈팅 게임으로 배우는 엔진 구조)

//
   

댓글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

Please turn AdBlock off

Notice for AdBlock users

Please turn AdBlock off