book/06-command-buffer-and-context.md

06. CommandBuffer와 ScriptableRenderContext

CommandBuffer 기록/실행 모델, 상태 누수 방지, RenderGraph와의 관계, 프로파일링 기본을 정리한다

06. CommandBuffer와 ScriptableRenderContext 구조

이 챕터는 “GPU 명령을 어떻게 구성하는가”를 다룹니다. RenderGraph를 써도 내부적으로는 CommandBuffer가 실행 단위이며, 호환 모드(Execute 경로)에서는 직접 다루게 됩니다.

6.1 CommandBuffer는 무엇인가?

CommandBuffer는 렌더링 명령의 리스트입니다.

  • 드로우 호출(DrawRenderer/DrawMesh)
  • 렌더 타겟 설정(SetRenderTarget)
  • 상태 설정(SetGlobal*, SetKeyword)
  • 블릿/풀스크린 패스

같은 명령을 “즉시” 실행하지 않고, 쌓아뒀다가 SRP가 적절한 타이밍에 제출합니다.

6.1.1 CommandBuffer를 “상태(State) + 드로우(Draw) + 복사(Copy)”로 나눠라

실무에서 CommandBuffer는 아래 3종 행동이 섞여 있습니다.

  1. 상태 설정
    • 전역 상수/키워드/렌더 상태(블렌드/뎁스/스텐실)에 해당
  2. 드로우 호출
    • DrawRenderers/DrawMesh/RendererList 등 실제 그리기
  3. 복사/풀스크린
    • Blit, 풀스크린 삼각형(포스트), 텍스처 변환/다운샘플 등

문제가 생길 때는 “상태가 누수”되는 경우가 많기 때문에, 패스 단위로 상태를 한정하려는 의식이 중요합니다.

6.2 CommandBufferPool

실무에서는 매 프레임 new CommandBuffer()를 하면 GC/할당 부담이 생깁니다. 그래서 Unity는 풀을 제공합니다.

C#
var cmd = CommandBufferPool.Get("MyPass");
try
{
    // cmd 기록
    context.ExecuteCommandBuffer(cmd);
}
finally
{
    CommandBufferPool.Release(cmd);
}

6.3 (중요) RenderGraph에서도 결국 cmd가 실행 단위다

RenderGraph 경로에서도 builder.SetRenderFunc((..., ctx) => { ... })ctx.cmd는 CommandBuffer입니다.

즉:

  • RenderGraph: “의존성/수명/순서”를 선언하는 상위 구조
  • CommandBuffer: “GPU에 제출되는 실제 명령”의 하위 실행 단위

둘을 함께 이해해야 디버깅이 가능합니다.

6.3 ScriptableRenderContext의 역할

ScriptableRenderContext는:

  • 컬링 수행
  • CommandBuffer 실행/제출
  • 렌더링 상태 관리

등을 담당합니다. SRP는 이 컨텍스트를 통해 렌더링 파이프라인을 실행합니다.

6.4 자주 나오는 실수

  • CommandBuffer를 기록했는데 ExecuteCommandBuffer를 호출하지 않음
  • 전역 상태(키워드/전역 텍스처)를 설정하고 해제하지 않음 → 다른 카메라/패스에 누수
  • RTHandle/RenderTexture 수명 관리를 패스 밖에서 중복 처리

6.5 렌더 타겟 설정(Load/Store)과 타일 GPU 감각

모바일/타일 기반 GPU에서는 “로드/스토어(메모리 왕복)”가 성능에 크게 영향을 줍니다.

개념적으로 렌더 타겟 바인딩에는:

  • LoadAction: 이전 내용을 읽어올지(Load) / 무시할지(DontCare/Clear)
  • StoreAction: 결과를 메모리에 저장할지(Store) / 버릴지(DontCare)

같은 개념이 있고, URP는 내부에서 이를 가능한 최적화하려고 합니다.

RenderGraph는 이런 최적화를 돕기 위해 “읽기/쓰기 선언”을 강제하는 측면도 있습니다. (관련: 04. RenderGraph)

6.6 프로파일링/디버깅: ProfilingScope를 습관화하라

패스가 늘어나면 “어디가 느린지”가 안 보입니다. Unity/URP는 프로파일링 샘플러를 제공합니다.

C#
using (new ProfilingScope(cmd, new ProfilingSampler("MyPass")))
{
    // cmd 기록
}

RenderGraph Viewer + Profiler를 같이 보면서:

  • 패스 수가 늘었는지
  • Blit/Copy가 과한지
  • 특정 패스가 예상보다 오래 걸리는지

를 점검하는 루틴을 권장합니다.

추가 읽을거리(공식/권위 자료)