성능 향상: 발로란트의 글로벌 무효화 기능

발로란트 성능 팀에서 플레이어 여러분의 클라이언트 성능을 향상한 방법을 설명해드립니다.

플레이어 여러분, 안녕하세요! 발로란트 성능 팀의 소프트웨어 엔지니어 아론 체니입니다. 성능은 발로란트에서 공정한 경쟁을 유지하는 데 중요한 역할을 하며 성능 팀에서는 서버와 클라이언트 성능의 관찰, 유지, 개선을 담당합니다.

몇 개월에 걸쳐 개발한 기능인 글로벌 무효화(Global Invalidation)를 소개해드리게 되어 신납니다. 기능의 세부 사항으로 넘어가기 전에 4.03 패치 이후 글로벌 무효화가 클라이언트 성능에 어떠한 영향을 끼쳤는지 살펴보고자 합니다.

거두절미하고 말씀드리자면 효과가 상당히 대단했습니다.

global-invalidation-live-data-kr_(1).jpg

글로벌 무효화는 플레이어 대부분에게 큰 성능 향상을 가져왔습니다. 사실 발로란트 출시 이래 가장 큰 폭의 성능 향상으로 이어졌습니다.

이러한 그래프와 결과가 만족스럽기는 하지만, 그래프가 무엇을 나타내는지 정확히 이해할 필요가 있습니다. 대량의 복잡한 데이터를 다뤄야 하므로 플레이어 경험을 이해하는 데 도움이 되도록 데이터를 정리, 처리, 관리합니다. 전체적으로 이해하려면 아래의 사항을 염두에 두어야 합니다.

  • 그래프의 축은 패치와 평균 FPS이며 수치가 높을수록 좋습니다.
  • 각각의 선은 플레이어층에서 흔한 하드웨어 구성(CPU와 GPU의 조합)을 나타냅니다. 성능 데이터를 분석할 때는 CPU와 GPU 조합이 성능의 예측에 가장 중요하다고 봅니다. CPU와 GPU의 조합이 같은 컴퓨터는 그래프에 묶어서 나타냈습니다.
  • 데이터 표본은 일반전과 경쟁전 등 두 게임 모드에서 추출했습니다. 일반전과 경쟁전은 발로란트의 2대 인기 게임 모드이므로 관련 부분을 이해하고 성능을 향상하기 위해 큰 노력을 기울입니다.
  • 접속한 플레이어가 정확히 10명이 아니었던 게임은 제외했습니다. 이는 특이 경우에 의해 데이터가 왜곡되지 않도록 하기 위함입니다(플레이어가 적은 게임의 성능이 더 낫습니다).

    글로벌 무효화 개요

    global-invalidation-summary-flow-kr_(1).jpg

    글로벌 무효화는 CPU에 의존한 클라이언트(보통 중간에서 높은 사양의 컴퓨터에 해당)에서 최대 15%까지의 성능 향상으로 이어졌습니다. 이러한 성과는 여러 팀이 수개월 동안 기울인 노력의 결과입니다. 게임에서 최적화의 여지가 큰 부분을 파악하는 작업에 결실이 따랐으며 그 과정에 걸쳐 위험을 관리한 덕분에 플레이어 경험의 안정성을 유지할 수 있었습니다.

    글로벌 무효화의 수혜자와 기대치 조율

    라이브 서버 지표에 따르면 글로벌 무효화 덕분에 CPU에 의존한 클라이언트(보통 중간에서 높은 사양의 컴퓨터에 해당) 구성에서 최대 15%의 성능 향상 효과가 있었습니다.

    전체를 놓고 보면 성능 향상 추세가 뚜렷하게 보이지만, 이러한 추세가 순간순간의 게임플레이를 대변하지는 못합니다. 또한 두 컴퓨터의 하드웨어가 같다고 해서 결과까지 같으리라는 보장은 없습니다.

    정리하자면 CPU에 의존한 컴퓨터에서 기준 성능은 대체로 향상되었지만, 실제 성능은 컴퓨터마다 고유한 여러 가지 기타 요인에 따라 달라질 수 있습니다.

    글로벌 무효화 이해하기

    글로벌 무효화를 정식으로 소개하기에 앞서 언리얼 엔진의 UI 요소를 간략하게 설명해드릴 필요가 있습니다.

    위젯과 트리 구조

    UI 요소(위젯이라고도 합니다)는 트리 구조에 따라 더 작은 기본 요소로 구성됩니다. 트리 구조는 컴퓨터의 파일 체계와 비슷합니다. 위젯에는 (폴더 하나에 많은 파일을 저장할 수 있듯이) 임의 개수의 자손이 딸릴 수 있습니다.

    이러한 기본 구성 요소를 결합하면 복잡한 위젯을 만들 수 있습니다. 예를 들어 잔여 탄약 표시는 여러 부분으로 이루어져 있으며 아래와 같은 트리 구조를 띱니다.

    전부 결합하면 잔여 탄약 표시의 구성 요소는 아래와 같이 보입니다.

    valorant-ui-elements-kr_(1).jpg

    (위의 붉은색 윤곽은 보기 쉽도록 과장되어 있습니다. 실제로는 겹치는 윤곽이 많습니다.)

    트리 구조에서 하나 이상의 위젯이 변경되면 다수의 다른 위젯에 영향이 미칠 수 있습니다. 예를 들어 화면에서 위젯 하나의 위치가 바뀌면 모든 하위 위젯의 위치도 다시 계산되어야 합니다. 이러한 변화의 관리는 다음 부분에서 다룰 ‘무효화’라는 체계에 의해 이루어집니다.

    무효화

    무효화란 언리얼 엔진에서 특정 위젯에 변화가 생겼으므로 갱신이 필요하다고 표시하는 처리 절차를 의미합니다.

    위젯을 무효화해야 하는 경우는 다양합니다. 게임 내 상황에 따라 애니메이션, 색깔, 불투명도, 크기, 순서, 텍스트, 이미지 등 다양한 변화가 위젯에 생길 수 있습니다. 위젯에 이러한 변화가 생기면 무효화되어야 합니다. 위젯을 갱신해야 한다는 의미죠.

    위젯 무효화는 여러 유형으로 나눌 수 있기 때문에 이러한 절차는 한층 더 복잡해집니다. 아래에서 몇 가지 유형을 소개해드리겠습니다.

      • 레이아웃 - 위젯의 크기가 변경되었을 때(여기에 따르는 비용은 매우 큽니다)
      • 페인트 - 위젯의 모습이 바뀌었지만, 크기는 그대로일 때
      • 자손 순서 - 트리 구조 내에서 위젯의 순서가 바뀌었을 때(동시에 레이아웃 무효화도 이루어져야 하므로 큰 비용이 따릅니다)
      • 가시성 - 위젯이 투명해지거나 나타나는 등 가시성이 바뀌었을 때(동시에 레이아웃 무효화도 이루어져야 하므로 큰 비용이 따릅니다)

    이러한 무효화 유형은 위젯을 제대로 그리는 데 필요한 작업을 보여줍니다.

    다른 위젯에 의존하는 위젯이 있으면 더더욱 복잡해집니다. 위젯은 계층 구조로 조직되어 있으며 위젯의 배열은 여러 요인에 따라 결정됩니다. 위젯이 제대로 그려지도록 하려면 위젯 한 개를 무효화하기 위해 여러 개의 연관 위젯을 무효화해야 할 수도 있습니다. 예를 들어 (친구 목록을 포함하는 소셜 패널처럼) 다수의 위젯이 수직 구조로 조직되어 있는데 (친구의 접속 등으로 인해) 위젯의 순서가 바뀌면 수직 구조에 속한 모든 위젯이 갱신되어야 합니다.

    이러한 체계에는 여러 가지 목표가 있습니다.

    • 무효화하는 위젯의 수를 최소화해야 합니다. 그러면 위젯이 제대로 그려지기 위해 갱신되어야 하는 위젯의 수가 줄어듭니다.
    • 꼭 필요할 때만 위젯을 무효화해야 합니다. 위젯을 부적절하게 무효화하면 귀중한 CPU 주기를 허비하게 됩니다.
    • 위젯이 무효화되지 않았으면 프레임마다 빠르게 그려지도록 결과를 캐시에 저장해야 합니다. 아무런 변화가 없었으면 CPU 주기를 아낄 수 있습니다.

      이만하면 위젯이 어떻게 갱신되고 어떠한 경우에 무효화가 발생하는지를 이해하기에 충분합니다. 이제 개발자들이 무효화를 실질적으로 어떻게 활용하는지 살펴보겠습니다.

      인밸리데이션 박스

      언리얼 엔진에서 제공되는 인밸리데이션(invalidation, 무효화) 박스라는 구성 요소를 활용하면 여러 위젯을 하나로 묶을 수 있습니다. 하나의 인밸리데이션 박스에 담긴 모든 위젯은 프리패스, 틱, 페인트 처리되지 않고 결과가 버텍스 버퍼에 저장됩니다.

      인밸리데이션 박스에 담긴 위젯 하나가 무효화되면 캐시에 저장된 데이터가 버려지고 위젯이 갱신된 후 다시 페인트 처리됩니다. 프레임 하나만 놓고 보면 캐시 갱신에 필요한 비용이 클 수 있지만, 길게 보면 비용을 분할했을 때의 결과가 훨씬 좋습니다.

      인밸리데이션 박스는 발로란트의 UI 성능 향상에 핵심적이며 출시를 준비하는 동안 더더욱 중요한 역할을 했습니다. 하지만 아무런 대가 없이 활용할 수 있는 것은 아닙니다.

      • 개발자는 인밸리데이션 박스로 묶기에 어떠한 위젯이 적합한지 알아야 합니다. 정기적으로 갱신되는 위젯은 적합하지 않습니다.
      • 위젯을 인밸리데이션 박스에 담으려면 개발자의 수작업이 필요합니다. 게임의 모든 위젯을 인밸리데이션 박스에 담기란 현실적으로 어렵습니다. 따라서 개발자는 인밸리데이션 박스에 넣을 만한 위젯이 무엇인지 알아야 합니다.

        인밸리데이션 박스에 대해 더 자세히 알고 싶다면 에픽게임즈의 기술 문서를 확인해보시기 바랍니다.

        이 정도 배경지식이면 글로벌 무효화를 살펴보기에 충분합니다!

        글로벌 무효화 소개

        이쯤이면 ‘그냥 모든 UI 요소를 포괄적인 인밸리데이션 박스 하나에 다 넣으면 되는 거 아닌가?’라고 생각하실 수 있습니다. 글로벌(포괄적인) 무효화의 기능이 바로 그겁니다.

        글로벌 무효화의 목표는 게임 전반에 걸쳐 UI 성능을 현저히 개선하는 동시에 각각의 인밸리데이션 박스에 위젯을 넣는 개발자의 수작업을 줄이는 것입니다. 두 마리 토끼를 잡는 셈이죠.

        하지만 언리얼 엔진 4.25(발로란트에서 사용하는 버전)에서는 글로벌 무효화가 일부 위젯 유형만 지원합니다. 이후 버전의 언리얼 엔진에서 개선이 이루어지기는 했지만, 발로란트는 개선사항의 효과를 바로 볼 수 없습니다. 또한 글로벌 무효화가 발로란트의 성능을 얼마나 향상할지 잘 몰랐습니다.

        본격적인 작업은 여기에서 시작되었습니다.

        글로벌 무효화 작업을 시작한 이유

        2021년 7월 말에 성능 팀은 내부 플레이테스트에서 글로벌 무효화의 테스트를 진행해보기로 했습니다. 플레이테스트가 성공적으로 마무리될 수 있도록 소규모 변경사항을 통해 몇몇 버그를 수정했습니다. 하지만 플레이테스트 도중 버그가 추가로 드러나겠다고 예상했으며... 확실히 버그가 있었습니다.

        플레이테스트가 끝날 때까지 발견한 버그를 세어 보니 약 20개였습니다. 명백한 버그만 셌는데도 그랬죠. 더 미묘하고 은근한 버그가 더 많이 도사리고 있을 가능성이 컸고 구체적으로 테스트하지 못한 여러 가지 특이 경우까지 생각해야 했습니다.

        그런데... 글로벌 무효화가 성능 향상으로 이어졌을까요? 확실히 효과가 있었습니다.

        플레이테스트에서 얻은 데이터를 분석해보니 UI가 약 35% 빨라졌음을 확인했습니다. (참고: UI는 프레임 하나에 따르는 비용의 일부에 불과합니다.)

        하지만 아직 의문점이 많이 남아있었습니다.

        • 모든 버그를 수정하기까지 시간이 얼마나 걸릴까?
        • 어느 팀(혹은 팀들)이 작업을 담당해야 할까?
        • 예정된 다른 작업보다 우선으로 처리해야 할까? 정기 콘텐츠 출시가 차질 없이 이루어질 수 있도록 작업 일정은 보통 몇 개월 전 미리 세우게 되며 글로벌 무효화처럼 예상하지 않은 작업이 생기면 아무리 구미가 당기더라도 일정에 넣기 어렵습니다.
        • 버그를 수정하면 성능 향상 효과가 줄어들까? 버그를 전부 수정하면 코드를 대거 변경해야 하며 각각의 변경사항은 UI 구현에 따르는 비용의 증가로 이어질 수 있습니다.
        • 작업을 언제 진행해야 할까? 이후 버전의 언리얼 엔진을 목표로 글로벌 무효화의 개발이 적극적으로 이루어지고 있음을 알았기 때문에 에픽게임즈의 변경사항을 발로란트에 통합하는 일정을 어떻게 할지 고려해야 했습니다.

          궁극적으로는 몇 가지 이유로 추진할 만한 작업이라고 판단했습니다.

          언리얼 엔진 통합과 작업 시기 고민

          언리얼 엔진 4.26과 4.27에서 글로벌 무효화에 큰 발전이 이루어졌지만, 발로란트에 엔진 변경사항을 통합할 때는 시차를 둡니다. ‘최첨단’ 버전을 사용하지 않는 이유는 위험 요소를 줄이고 플레이어 여러분에게 안정적인 경험을 선사해드리고자 하기 때문입니다.

          일정에 따르면 몇 개월 동안 언리얼 엔진 4.25를 계속 사용해야 했으므로 플레이어들이 성능 향상을 누릴 수 있을 때까지 1년을 훌쩍 뛰어넘는 시간을 기다려야 했습니다. 성에 안 차는 상황이었습니다.

          발로란트 팀이 언리얼 엔진 업그레이드를 어떻게 생각하는지가 궁금하면 발로란트 기술 리드 마커스 리드 님의 트위터 스레드(영문 링크)를 확인해보시기 바랍니다.

          확인된 성능 향상

          글로벌 무효화는 측정치를 확인할 수 있다는 점에서 성능 관련 작업으로서 정말 독특합니다. 내부 플레이테스트 동안 잠재적인 성능 향상 효과를 측정했으며 그 측정치를 실현하는 방법이 (대체로) 명확하게 보였습니다.

          성능 향상 작업은 어렵습니다. 비약적인 발전보다는 한 걸음씩 나아가야 하며 점진적인 변화가 시간에 걸쳐 전반적인 성능 향상으로 이어지죠. 단일 변경사항으로 두 자릿수의 개선 효과를 내는 일은 흔하지 않습니다. 글로벌 무효화는 추진하지 않기에는 너무 탐나는 최적화 기회였습니다.

          버그 수정으로 인한 향상 효과 감소를 고려하더라도 글로벌 무효화는 합리적인 시간 안에 플레이어에게 현저한 성능 향상을 선사할 최고의 기회였습니다.

          글로벌 무효화 도입에 성공한 방법

          글로벌 무효화와 관련된 초기 실험은 2021년 7월 말에 시작했지만, 2021년 9월 말이 되어서야 기능을 안정화하기 위한 노력을 본격적으로 시작했습니다.

          에픽게임즈 변경사항 선별 도입

          언리얼 엔진 4.26 및 4.27을 발로란트에 완전히 통합하는 방안은 고려할 수 없었지만, 에픽게임즈가 글로벌 무효화의 개발을 적극적으로 진행했다는 사실을 알고 있어서 안정적이면서 완전하게 작동하는 글로벌 무효화를 완성하는 데 도움이 될 만한 변경사항을 파악하고자 수천 개의 변경사항을 살펴보기로 했습니다.

          변경사항을 부분적으로만 통합하는 작업은 쉽지 않았습니다. 안정성 유지를 위해 엔진의 핵심 기능은 최소한으로만 바꾸면서 적절한 변경사항을 들여와야 했습니다. 다른 개발자에게 영향을 주지 않도록 이러한 작업은 전부 발로란트의 주된 브랜치가 아니라 별도의 브랜치에서 이루어졌습니다.

          에픽게임즈의 변경사항을 선별적으로 발로란트의 엔진에 통합한 후 몇 주 동안 최대한 많은 버그를 해결하며 주된 발로란트 브랜치에 글로벌 무효화를 도입할 준비를 했습니다. 그러면서 (심각한 문제가 발생했을 때를 대비해) 기능 활성화 여부를 빠르게 바꾸는 기능을 추가하기도 했습니다.

          많은 버그를 수정하고 에픽게임즈가 언리얼 엔진 4.26 및 4.27에 도입한 변경사항 다수를 통합해서 별도 브랜치를 주된 브랜치에 병합했습니다.

          버그 간 공통점 찾기

          글로벌 무효화 관련 버그는 다양한 방식으로 나타났지만, 근본적인 원인은 보통 하나의 문제였습니다. 변경사항 하나만으로 여러 문제를 해결할 수 있기 때문에 이러한 버그는 가치가 큽니다. 예를 들어 게임 곳곳에서 발생하던 10여 가지의 버그를 한꺼번에 해결한 변경사항이 있었습니다. 근본적인 문제를 면밀히 분석하면 글로벌 무효화의 안정성을 개선하는 탄탄한 해결책을 도출할 수 있었습니다.

          버그 발견 후 해결하고 플레이테스트 반복

          이어서 몇 개월 동안 플레이테스트 전에 글로벌 무효화를 활성화하고 다수의 버그를 발견한 후 플레이테스트가 끝나면 글로벌 무효화를 비활성화하고 버그를 수정하는 작업을 주기적으로 반복했습니다.

          developer-bug-fix-flow-kr_(1).jpg

          주기를 반복할 때마다 보고되는 버그가 줄어들었습니다. 꾸준히 나오던 버그가 점점 줄어들기 시작하고 결국 보고되는 버그가 없어질 때까지 주기를 계속 반복했습니다.

          2021년 11월 말에 이르러서는 주요 문제를 모두 해결했고 글로벌 무효화는 대체로 안정적이었습니다.

          언급할 만한 버그
          • 채팅 음향 효과 무한 반복 - 메뉴에 있을 때 채팅 바에 마우스를 올리면 소리가 재생됩니다. 짜증나게도 버그로 인해 그 음향 효과가 1초에 여러 번 재생되었습니다. 버그를 수정하려면 위젯이 한 프레임에 여러 번의 마우스 이벤트를 인식하는 시점을 이해할 필요가 있었습니다.
          • 아스트라로 인한 게임 충돌 - 한때는 게임 로딩 시 모든 아스트라 플레이어가 충돌을 겪었습니다. 에픽게임즈의 변경사항을 통합한 후 버그가 수정되었습니다.
          • 다중 상속 충돌 - C++에서 다중 상속은 다루기 까다롭습니다. 너무 자세히 들어가지 않고 설명해드리자면 특정 클래스의 소멸자들이 올바른 순서에 따라 실행되지 않아서 충돌을 야기했습니다. 단순히 코드 두 줄을 맞바꿔서 상속 순서를 변경하니 문제가 해결되었습니다. 다중 상속에 대해 더 자세히 알고 싶다면 여기(영문 링크)를 확인해보시기 바랍니다.

          테스트 방법

          글로벌 무효화를 준비할 때 특히 조심스러웠던 이유(이자 테스트를 철저하게 진행한 이유)는 말 그대로 게임의 모든 측면에 영향을 주기 때문입니다.

          친구 목록이요? 물론입니다. 대기열에 진입할 때 누르는 버튼이요? 마찬가지입니다. 설정 메뉴요? 당연하죠. 제 헤드샷 비율은요? 그게...

          아무튼 UI 요소는 게임 곳곳에 있으며 플레이어에게 중요한 정보를 전달합니다. UI 요소가 하나라도 망가지는 것은 용납할 수 없었습니다.

          그래서 글로벌 무효화가 의도한 대로 작동하는지 확신할 수 있도록 QA(품질보증) 팀에서 다양한 전략을 아우르는 테스트 계획을 세웠습니다.

          수직 단면 테스트

          발로란트의 ‘수직 단면’은 클라이언트 실행부터 대기열 진입 후 게임을 처음부터 끝까지 플레이하고 게임 종료 화면과 상호작용하기까지 플레이어가 일반적으로 밟는 주요 과정을 의미합니다. QA 담당자들은 게임의 핵심 요소에 집중함으로써 게임에서 가장 많이 사용되는 부분을 빠르게 테스트하고 문제를 초기에 발견할 수 있었습니다.

          파괴 테스트

          수직 단면 테스트에 이어서 파괴 테스트를 진행합니다. 파괴 테스트로는 보통 외부 요인 변경(네트워크 핑, 프레임률, Alt+Tab으로 작업 전환 등)을 통해 예상하지 못한 문제를 발견하고자 합니다. QA 팀은 사내 툴을 활용해 몇 주 동안 파괴 테스트를 진행했습니다.

          특이 경우 테스트

          발로란트에는 전체 플레이어의 작은 일부만 경험하는 부분이 많습니다. 신규 플레이어 경험처럼 게임의 어떤 부분은 딱 한 번만 경험할 수 있습니다. 경험할 기회가 적다고 해서 그 부분이 덜 중요하다는 의미는 아닙니다. 특이 경우를 모두 파악하고 테스트한 덕분에 숨겨진 오류를 찾아낼 수 있었습니다.

          PBE 테스트

          PBE(Public Beta Environment, 공개 베타 환경) 테스트는 글로벌 무효화를 준비하는 과정에서 중요한 단계였습니다.

          • PBE 테스트는 외부 플레이어가 처음으로 글로벌 무효화를 테스트할 기회였습니다. 글로벌 무효화를 ‘실제’ 상황에서 테스트할 수 있다는 의미였습니다.
          • PBE 테스트 서버에서는 다양한 사양의 하드웨어를 살펴볼 수 있으며 의도적으로 저샤앙부터 고사양까지의 컴퓨터를 포괄하고자 합니다. 폭넓은 하드웨어에서 테스트를 진행한 덕분에 글로벌 무효화가 전체 플레이어층에 걸쳐 어느 정도의 효과로 이어질지에 대해 자신감을 얻을 수 있었습니다.

            2022년 1월 22일에서 23일까지 주말에 걸쳐 PBE 테스트를 진행한 결과 글로벌 무효화가 무결성에 악영향을 끼치지 않고 예상에 들어맞는 수준의 성능 향상으로 이어짐을 확신할 수 있었습니다.

            글로벌 무효화 정식 도입

            4.03 패치에서 글로벌 무효화를 도입한 후 버그가 있는지 확인하기 위해 플레이어 신고를 꼼꼼하게 살펴봤습니다. 또한 결과가 예상에 부합하는지 확인하고자 성능 데이터를 예의 주시했습니다. 궁극적으로 글로벌 무효화는 대성공이었으며 플레이어 여러분이 개선된 프레임 시간에 만족하시기를 바랍니다.

            글로벌 무효화가 세상에 나왔으니 이제 성능 팀은 성능을 더욱더 향상하기 위한 작업을 계속하고자 합니다. 그럼 다음에 인사드릴 때까지 발로란트에서 즐거운 시간 되시길 바랍니다!