Unity 6에서 새롭게 제공하는 Shader 관련 기능들인 Shader Graph Heatmap Color Mode와 GraphicsStateCollection 대해 소개합니다.
이 두 기능은 그래픽스와 렌더링 효율성을 크게 향상시키기 위해 도입되었으며, 개발자들이 Shader Graph 및 최신 그래픽 API를 더 효과적으로 활용할 수 있도록 돕습니다. 이 아티클에서는 이러한 두 가지 새로운 기능을 상세하게 설명하고, 각각의 원리와 사용 방법, 그리고 개발에 어떤 도움이 되는지에 대해 다룹니다.
1. Shader Graph Heatmap Color Mode
Heatmap Color Mode는 Unity6에서 새롭게 제공되는 모드입니다. Shader Graph의 노드 중 어떤 노드의 비용이 큰지 직관적으로 표시해 주는 목적으로 추가된 모드입니다. Color Mode 드롭다운 메뉴에서 Heatmap을 선택하여 사용하실 수 있습니다.
Color Mode
Shader Graph는 Color Mode 기능을 제공하고 있습니다. 우측 상단에 있는 Color Mode 드롭다운 메뉴를 통해 기능에 접근할 수 있으며, 노드의 이름 아래에 색상을 표시하는 방식으로 구현되어 있습니다.
Unity 6 이전에는 노드의 종류를 색상으로 구분하는 Category, 정밀도에 따라 색상으로 구분하는 Precision, 사용자가 직접 정의할 수 있는 User Defined 3개의 모드가 제공되었습니다. 이번에 유니티6이 출시되면서 노드의 비용을 색상으로 구분하는 Heatmap 모드가 추가되었습니다.
Heatmap Color Mode
Heatmap Color Mode는 노드의 비용을 색상으로 표시하는 기능을 제공합니다. 어두운 색상일수록 GPU 계산 비용이 적으며, 밝은 색상일수록 GPU 계산 비용이 많이 듭니다. 표시된 색상을 통해, 배치된 노드 중 비용이 많이 드는 노드를 직관적으로 확인할 수 있습니다.
실시간으로 비용을 측정해서 보여주는 디버깅용 기능은 아니며, 노드의 비용을 미리 측정하여 측정된 값을 범위로 구분하고, 구분한 범위를 각각 다른 색상으로 표시해 주는 기능입니다.
Heatmap Color Mode에서 색상을 구분하는 원리
어떻게 색상을 결정해서 노드에 표시할 수 있는 것일지 알아보겠습니다. 유니티는 기본으로 제공하는 노드에 대해 미리 GPU 계산 비용을 측정하여 10개의 카테고리로 구분하였습니다.
비용을 측정하기 어려운 노드의 경우에는 0번 카테고리로 구분하고 있습니다. 0번 카테고리는 비용을 알 수 없는 노드를 모아놓은 것이 때문에 같은 0번 카테고리의 노드더라도, 비용이 같다고 판단해서는 안됩니다.
각 노드의 GPU 계산 비용(GPU Cycles)은 팀 존스의 Shader Playground2 사이트에서 측정했습니다. Radeon GPU Analyzer로 설정하여 컴파일 하고 ISA Breakdown를 사용하여 값을 집계했습니다. AMD GPU 기준으로 정확한 비용이라고 할 수 있고, GPU에 따라 다소 차이가 있을 수 있습니다. 하지만 카테고리를 큰 범위로 분류하고 있기 때문에 대략적인 비용을 확인하는 데는 충분합니다.
노드 카테고리에 따른 색상 표시의 예
0 ~ 10 Category에 해당하는 노드들을 직접 꺼내서 색상을 확인한 예제입니다. 비용이 동일한 노드는 색상도 동일합니다. 예를 들어, 7 Category에 해당하는 Hue와 Dither는 동일한 색상입니다.
노드의 카테고리를 확인하는 방법
유니티에서 기본으로 제공하는 노드 비용은 Heatmap with Default Values 에셋을 생성하여 확인할 수 있습니다.
생성된 에셋에서 Nodes에 작성된 목록을 참고해서 확인할 수 있습니다. 예를 들어, Absolute의 카테고리는 1번이며, 가장 어두운 색상으로 표시됩니다.
Heatmap 색상을 원하는 색상으로 변경하는 방법
지금까지 설명한 Heatmap은 Unity에서 기본으로 제공하는 색상입니다. 기본적으로는 11개의 카테고리와 11개의 색상으로 분류하고 있지만, 원하는 개수의 카테고리와 원하는 색상으로 변경할 수 있습니다.
먼저 Project Setting > ShaderGraph > Heatmap Color Mode Settings > Custom Values에 에셋을 설정해야 합니다. 여기에서는 위에서 만든 에셋으로 설정했습니다.
위에서 생성한 Heatmap Value 에셋의 Categories에 색상이 정의되어 있는 것을 확인할 수 있습니다. 여기서 카테고리의 개수를 수정할 수 있으며, 색상도 수정할 수 있습니다.
예를 들어 카테고리를 하나 추가한다고 가정하겠습니다. 11번 카테고리에 녹색을 추가했습니다.
그리고 11번 값을 사용하는 노드를 하나 설정하겠습니다. 기본 제공되는 카테고리가 1인 Absolute를 11로 변경했습니다.
Absolute의 Heatmap 색상이 11번 카테고리의 색상으로 변경되었습니다. 이런 방법으로 카테고리를 추가하고 원하는 색상으로 변경하여 사용할 수 있습니다.
정리
이 기능을 사용하면 비용이 큰 노드의 색상을 직관적으로 확인할 수 있으며, 프로젝트에 알맞게 카테고리 개수와 색상을 수정하여 사용할 수 있습니다.
2. GraphicsStateCollection
Unity 6에서는 최신 그래픽 API 기능을 더 잘 활용하기 위해 Pipeline State Object(이하 PSO) 수집 및 웜업을 위한 새로운 기능을 제공합니다.
최신 그래픽 API(DX12, Vulkan, Metal)에서는 PSO 생성 중에 런타임 셰이더 컴파일이 수행됩니다. 런타임 셰이더 컴파일 및 PSO 생성은 비용이 많이 들기 때문에 렌더링 시 눈에 띄는 끊김 현상이 발생하는 경우가 많습니다.
새롭게 제공하는 GraphicsStateCollection API는 런타임에 생성된 PSO를 추적하여 디스크에 정보를 저장하는 데 사용하는 기능입니다. 그런 다음 런타임에 컬렉션을 로드하고, 동기(시작/로드 화면 중) 로딩 또는 비동기(배경) 로딩을 통해 필요한 시점에 PSO를 미리 로딩할 수 있습니다.
Pipeline State Objects
PSO는 최신 그래픽스 API(DX12, Vulkan, Metal)에서 지원하는 기능입니다. 하지만 렌더링이 필요한 시점에 PSO를 생성하게 되기 때문에 병목이 발생할 수 있습니다.
GraphicsStateCollection
GraphicsStateCollection은 장면에서 사용될 PSO 정보를 미리 수집하여 컬렉션 정보를 저장합니다. 그리고 런타임에서는 컬렉션 정보를 가지고 원하는 시점에 동기/비동기 로딩을 통해 PSO를 원하는 시점에 생성할 수 있도록 도와줍니다. 최신 API에서 사용하고 있는 PSO 생성 병목 해소를 위해 GraphicsStateCollection을 사용해야 합니다.
PSO Tracing
PSO를 수집하기 위해서는 PSO를 생성할 것으로 예상되는 장면에 방문해야 합니다. 아래의 순서로 PSO 정보를 추적하고 저장할 수 있습니다.
GraphicsStateCollection 생성
수집을 시작할 지점에서 BeginTrace() 호출
수집이 필요한 장면 방문
수집을 종료할 지점에서 EndTrace() 호출
수집한 내용을 파일로 저장하기 위해 SaveToFile() 호출
using UnityEngine; using UnityEngine.Experimental.Rendering;
public class SaveToFileExample : MonoBehaviour { public GraphicsStateCollection graphicsStateCollection; public string filePath;
void Start() { graphicsStateCollection = new GraphicsStateCollection(); graphicsStateCollection.BeginTrace(); }
void OnDestroy() { graphicsStateCollection.EndTrace(); graphicsStateCollection.SaveToFile(filePath); } } |
GraphicsStateCollection 에셋
수집을 완료하고 나면, .graphicsstate 확장자의 에셋이 생성됩니다. 수집은 플랫폼마다 별도로 진행해야 하며, 플랫폼을 구분할 수 있는 정보는 GraphicsStateCollection의 runtimePlatform 변수에 저장되어 있습니다.
PSO Precooking
GraphicsStateCollection 에셋에는 방문했던 장면에서 생성하는 PSO 정보가 들어 있습니다. 병목을 줄이기 위해서는, 해당 장면에 방문하기 전에 PSO를 미리 생성해야 합니다.
아래 예제에서는 GraphicsStateCollection 객체에 정보가 미리 들어있다고 가정하고 있습니다. 그리고 아래의 예제 코드는 유니티 공식 매뉴얼에서도 확인할 수 있습니다.
GraphicsStateCollection의 객체 정보를 런타임에 직접 로딩하고 싶은 경우, 파일 경로를 받는 생성자를 사용할 수 있습니다. 아래의 생성자를 사용하면 미리 저장한 컬렉션 에셋 파일에서 정보를 가져올 수 있습니다.
public GraphicsStateCollection(string filePath); |
컬렉션에서 PSO를 생성하기 위해 WarmUp() 함수가 제공됩니다.
public Unity.Jobs.JobHandle WarmUp(Unity.Jobs.JobHandle dependency); public Unity.Jobs.JobHandle WarmUpProgressively(int count, Unity.Jobs.JobHandle dependency); |
위 함수 모두 JobHandle을 반환하며, 동기/비동기 방식으로 로딩하도록 구현할 수 있습니다. 예를 들어, 동기 로딩의 경우 handle.Complete()를 호출하여 즉시 로딩할 수 있습니다.
public class WarmUpSynchronousExample : MonoBehaviour { public GraphicsStateCollection graphicsStateCollection;
void Start() { JobHandle handle = graphicsStateCollection.WarmUp(); handle.Complete(); } } |
비동기 로딩의 경우, 아래와 같이 Job을 사용하여 구현할 수 있습니다.public class WarmUpSynchronousExample : MonoBehaviour { struct PostWarmUpJob : IJob { public void Execute() { Debug.Log("WarmUp is complete"); } }
public GraphicsStateCollection graphicsStateCollection; public JobHandle inputJobHandle;
void Start() { JobHandle handle = graphicsStateCollection.WarmUp(inputJobHandle);
var job = new PostWarmUpJob(); job.Schedule(handle); } } |
성능 비교
유니티에서 제공하는 샘플 중 URP Sample - Garden을 기준으로 성능을 확인했습니다, PSO를 캐시했을 때와 하지 않았을 때의 성능을 비교하면 아래와 같았습니다. 아래의 영상에서 성능 비교하는 과정을 확인할 수 있습니다.
정리
본 아티클에서는 Unity 6에서 최신 그래픽스 API가 제공하고 있는 PSO 기능을 어떻게 지원하고 있는지 알아보았습니다. PSO의 병목을 해소할 수 있도록 GraphicsStateCollection 기능을 제공하고 있으며, 이 기능을 사용하여 특정 장면에서 필요한 PSO 정보를 미리 수집할 수 있습니다. 또한, 수집한 정보를 통해 PSO의 생성을 원하는 시점에 수행함으로써 병목을 예방할 수 있다는 것을 확인하였습니다.