์นดํ…Œ๊ณ ๋ฆฌ ์—†์Œ

Unity URP ์ปค์Šคํ…€ HLSL ์…ฐ์ด๋”์˜ SRP Batcher ํ˜ธํ™˜์„ฑ ํŒŒ๊ดด ์›์ธ๊ณผ CBUFFER ๋ฉ”๋ชจ๋ฆฌ ๋ ˆ์ด์•„์›ƒ ์ตœ์ ํ™” ๊ฐ€์ด๋“œ

๊ฒŒ์ž„๊ต์ˆ˜ 2026. 9. 6. 22:01
๋ฐ˜์‘ํ˜•

๐ŸŒ English Abstract

This article explores the technical root causes of draw call spikes when custom HLSL shaders break Scriptable Render Pipeline (SRP) Batcher compatibility in Unity Universal Render Pipeline (URP). It provides an in-depth troubleshooting guide for diagnosing incompatible materials via Frame Debugger and refactoring Constant Buffers (CBUFFER). Furthermore, it covers exact 16-byte memory layout alignment rules in HLSL to optimize GPU memory access and ensure maximum rendering efficiency.

์œ ๋‹ˆํ‹ฐ(Unity)์˜ Universal Render Pipeline(URP) ํ™˜๊ฒฝ์—์„œ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•  ๋•Œ, ๊ทธ๋ž˜ํ”ฝ์Šค ์„ฑ๋Šฅ ์ตœ์ ํ™”์˜ ํ•ต์‹ฌ์€ ๋‹จ์—ฐ SRP Batcher(Scriptable Render Pipeline Batcher)์ž…๋‹ˆ๋‹ค. SRP Batcher๋Š” ๋™์ผํ•œ ์…ฐ์ด๋” ๋ณ€ํ˜•(Shader Variant)์„ ์‚ฌ์šฉํ•˜๋Š” ์„œ๋กœ ๋‹ค๋ฅธ ๋จธํ‹ฐ๋ฆฌ์–ผ(Material) ๊ฐ„์˜ ๋ฐ”์ธ๋”ฉ ์˜ค๋ฒ„ํ—ค๋“œ๋ฅผ ๊ทน์ ์œผ๋กœ ์ค„์—ฌ CPU ๋ Œ๋”๋ง ๋ณ‘๋ชฉ์„ ํ•ด๊ฒฐํ•ด ์ค๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์…ฐ์ด๋” ๊ทธ๋ž˜ํ”„(Shader Graph) ๋Œ€์‹  ์ปค์Šคํ…€ HLSL ์…ฐ์ด๋” ์ฝ”๋“œ๋ฅผ ์ง์ ‘ ์ž‘์„ฑํ•˜์—ฌ ์ ์šฉํ•˜๋Š” ์ˆœ๊ฐ„, ์ •์ฒด๋ถˆ๋ช…์˜ ๋“œ๋กœ์šฐ ์ฝœ(Draw Call / Batch Count) ๊ธ‰์ฆ ํ˜„์ƒ์„ ๊ฒช๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋นˆ๋ฒˆํ•˜๊ฒŒ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์ž‘์„ฑํ•œ HLSL ์ฝ”๋“œ ๋‚ด์˜ **CBUFFER(Constant Buffer)** ๊ตฌ์กฐ๋‚˜ ๋ณ€์ˆ˜ ์„ ์–ธ์ด SRP Batcher ํ˜ธํ™˜์„ฑ ์กฐ๊ฑด์— ์–ด๊ธ‹๋‚˜ ๋ฐฐ์นญ์ด ์ผ์‹œ์— ํ•ด์ œ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๋ณธ ํฌ์ŠคํŒ…์—์„œ๋Š” ์ปค์Šคํ…€ HLSL ์—ฐ๋™ ์‹œ SRP Batcher ํ˜ธํ™˜์„ฑ์ด ํŒŒ๊ดด๋˜๋Š” ์›์ธ์„ ๋‹ค๊ฐ๋„๋กœ ๋ถ„์„ํ•˜๊ณ , ํ”„๋ ˆ์ž„ ๋””๋ฒ„๊ฑฐ(Frame Debugger)๋ฅผ ํ†ตํ•œ ๋ฌธ์ œ ์ง„๋‹จ ๋ฐ CBUFFER ๋ฉ”๋ชจ๋ฆฌ ๋ ˆ์ด์•„์›ƒ ์ •๋ ฌ(Memory Layout Alignment) ์ตœ์ ํ™” ๊ธฐ๋ฒ•์„ ์‹ค๋ฌด ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… ๊ด€์ ์—์„œ ๊นŠ์ด ์žˆ๊ฒŒ ๋‹ค๋ฃน๋‹ˆ๋‹ค.



1. SRP Batcher ์ž‘๋™ ์›๋ฆฌ์™€ ํ˜ธํ™˜์„ฑ ํ•„์ˆ˜ ์กฐ๊ฑด

๋ฌธ์ œ ํ•ด๊ฒฐ์— ์•ž์„œ SRP Batcher๊ฐ€ ๋‚ด๋ถ€์ ์œผ๋กœ ์–ด๋–ป๊ฒŒ ๋“œ๋กœ์šฐ ์ฝœ์„ ์ค„์ด๋Š”์ง€ ์ดํ•ดํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ์กด์˜ ๋ Œ๋”๋ง ๋ฐฉ์‹์€ ์˜ค๋ธŒ์ ํŠธ๋งˆ๋‹ค ๋จธํ‹ฐ๋ฆฌ์–ผ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฐ”๋€” ๋•Œ๋งˆ๋‹ค CPU๊ฐ€ GPU ๋ช…๋ น ๋ฒ„ํผ(Command Buffer)์— GPU ์ƒํƒœ ๋ณ€๊ฒฝ(State Change)์„ ๊ธฐ๋กํ•˜๊ณ  Constant Buffer๋ฅผ ์žฌ๋ฐ”์ธ๋”ฉํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๊ทน์‹ฌํ•œ CPU ๋ฐ”์šด๋“œ(CPU-bound) ๋ณ‘๋ชฉ์„ ์œ ๋ฐœํ•ฉ๋‹ˆ๋‹ค.

SRP Batcher๋Š” ๋จธํ‹ฐ๋ฆฌ์–ผ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋”๋ผ๋„ GPU ๋ฉ”๋ชจ๋ฆฌ ์˜์—ญ(CBUFFER)์— ๋ฏธ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ์˜ฌ๋ ค๋‘๊ณ , ๋“œ๋กœ์šฐ ์ฝœ ํ˜ธ์ถœ ์‹œ ์ „์†กํ•˜๋Š” ๋ฐ์ดํ„ฐ ๋ฒ„ํผ ์œ„์น˜ ๋ณ€๊ฒฝ ๋ช…๋ น์–ด๋งŒ ๋น ๋ฅด๊ฒŒ ์ „์†กํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๋ ค๋ฉด ์ปค์Šคํ…€ HLSL ์…ฐ์ด๋”๊ฐ€ ๋‹ค์Œ ๋‘ ๊ฐ€์ง€ ํ•„์ˆ˜ ์กฐ๊ฑด์„ ์™„๋ฒฝํ•˜๊ฒŒ ์ถฉ์กฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • UnityPerMaterial ์ƒ์ˆ˜ ๋ฒ„ํผ ๋ธ”๋ก ์‚ฌ์šฉ: ๋จธํ‹ฐ๋ฆฌ์–ผ์˜ ๋ชจ๋“  ๋…ธ์ถœ ์†์„ฑ(Properties)์€ ๋ฐ˜๋“œ์‹œ CBUFFER_START(UnityPerMaterial)๊ณผ CBUFFER_END ๋งคํฌ๋กœ ๋ธ”๋ก ๋‚ด๋ถ€์— ์„ ์–ธ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • UnityPerDraw ์ƒ์ˆ˜ ๋ฒ„ํผ ์œ ์ง€: ๋ณ€ํ™˜ ํ–‰๋ ฌ(Matrix) ๋“ฑ ์˜ค๋ธŒ์ ํŠธ ๋‹จ์œ„์˜ ๋ฐ์ดํ„ฐ๋Š” Engine ํ‘œ์ค€ UnityPerDraw ๋ฒ„ํผ ๋‚ด์— ์ •์˜๋˜์–ด์•ผ ํ•˜๋ฉฐ, ์ด๋Š” URP ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ—ค๋”(Core.hlsl) ํฌํ•จ์„ ํ†ตํ•ด ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค.

๋งŒ์•ฝ ์…ฐ์ด๋” ์ฝ”๋“œ ๋‚ด์—์„œ ๋‹จ ํ•˜๋‚˜์˜ ๋จธํ‹ฐ๋ฆฌ์–ผ ๋ณ€์ˆ˜๋ผ๋„ UnityPerMaterial CBUFFER ์™ธ๋ถ€์— ๊ฐœ๋ณ„ ์„ ์–ธ๋˜๊ฑฐ๋‚˜, ๋ณ€์ˆ˜ ๋ฐ์ดํ„ฐ ํƒ€์ž… ํฌ๊ธฐ ๋ฐ ์ •๋ ฌ ์ˆœ์„œ๊ฐ€ ์–ด๊ธ‹๋‚˜๋ฉด ์œ ๋‹ˆํ‹ฐ ์—”์ง„์€ ์ฆ‰์‹œ ํ•ด๋‹น ๋จธํ‹ฐ๋ฆฌ์–ผ์˜ SRP Batcher ํ˜ธํ™˜์„ฑ์„ "Not Compatible"๋กœ ํŒ์ •ํ•˜๊ณ  ๋ฐฐ์นญ ๋Œ€์ƒ์—์„œ ์ œ์™ธํ•ฉ๋‹ˆ๋‹ค.

2. SRP Batcher ํŒŒ๊ดด ์ง„๋‹จ ๋ฐ ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… ๋‹จ๊ณ„

ํ”„๋กœ์ ํŠธ์—์„œ ๋“œ๋กœ์šฐ ์ฝœ์ด ๊ธ‰์ฆํ–ˆ์„ ๋•Œ, ์…ฐ์ด๋” ํ˜ธํ™˜์„ฑ์„ ๋น ๋ฅด๊ฒŒ ์ง„๋‹จํ•˜๊ณ  ์›์ธ์„ ํŒŒ์•…ํ•˜๋Š” ๋‹จ๊ณ„์  ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

Step 1: Material Inspector์—์„œ ํ˜ธํ™˜์„ฑ ์ƒํƒœ ํ™•์ธ

๋ฌธ์ œ๊ฐ€ ์˜์‹ฌ๋˜๋Š” ๋จธํ‹ฐ๋ฆฌ์–ผ์„ ์„ ํƒํ•˜๊ณ  ์ธ์ŠคํŽ™ํ„ฐ(Inspector) ์ฐฝ ์ƒ๋‹จ์˜ Shader ๋””๋ฒ„๊ทธ ์˜์—ญ์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ์ธ์ŠคํŽ™ํ„ฐ ์šฐ์ธก ์ƒ๋‹จ์˜ ๋ฌด๊ธฐ ๋ชจ์–‘ ์•„์ด์ฝ˜์„ ๋ˆŒ๋Ÿฌ Inspector๋ฅผ Debug ๋ชจ๋“œ๋กœ ์ „ํ™˜ํ•˜๊ฑฐ๋‚˜, ์…ฐ์ด๋” ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ์„ ํƒํ–ˆ์„ ๋•Œ ๋‚˜ํƒ€๋‚˜๋Š” ์†์„ฑ์ฐฝ์—์„œ ํ˜ธํ™˜์„ฑ ์—ฌ๋ถ€๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • SRP Batcher: Compatible โ†’ ์ •์ƒ ์ž‘๋™ ์ค‘
  • SRP Batcher: Not Compatible โ†’ CBUFFER ๊ตฌ์กฐ ๋ฏธ๋น„ ๋˜๋Š” ๋น„ํ˜ธํ™˜ ๊ตฌ๋ฌธ ์กด์žฌ

Step 2: Frame Debugger(ํ”„๋ ˆ์ž„ ๋””๋ฒ„๊ฑฐ)๋ฅผ ํ†ตํ•œ ๋ Œ๋”๋ง ๋ฐฐ์น˜ ๋ถ„์„

Window > Analysis > Frame Debugger๋ฅผ ์—ด๊ณ  ํ”„๋ ˆ์ž„์„ ์บก์ฒ˜(Capture)ํ•ฉ๋‹ˆ๋‹ค. ๋ Œ๋” ํŠธ๋ฆฌ ์˜์—ญ์—์„œ ๋“œ๋กœ์šฐ ์ฝœ ๋ชฉ๋ก์„ ํ™•์ธํ•  ๋•Œ RenderLoopDevice.DrawSRPBatcher ํ•ญ๋ชฉ์œผ๋กœ ๋ฌถ์ด์ง€ ์•Š๊ณ  Draw Mesh (Instanced)๋‚˜ ์ˆ˜๋งŽ์€ ๋‹จ์ผ Draw Dynamic์œผ๋กœ ํŒŒํŽธํ™”๋˜์–ด ์žˆ๋‹ค๋ฉด, ์›์ธ์€ ์…ฐ์ด๋”์— ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿšจ ์‹ค๋ฌด ์—ฃ์ง€ ์ผ€์ด์Šค (Edge Case Warning):
์Šคํฌ๋ฆฝํŠธ ์ƒ์—์„œ MaterialPropertyBlock(MPB)์„ ์‚ฌ์šฉํ•˜์—ฌ ๋Ÿฐํƒ€์ž„์— ๋จธํ‹ฐ๋ฆฌ์–ผ ์†์„ฑ์„ ๋ณ€๊ฒฝํ•˜๋ฉด, ํ•ด๋‹น ์ธ์Šคํ„ด์Šค๋Š” SRP Batcher์˜ CBUFFER ๋ฉ”๋ชจ๋ฆฌ ์—ฐ์†์„ฑ์„ ํŒŒ๊ดดํ•˜๋ฏ€๋กœ SRP Batcher์—์„œ ๊ฐ•์ œ๋กœ ํƒˆ๋ฝ๋ฉ๋‹ˆ๋‹ค. MPB ๋Œ€์‹  GPU Instancing์„ ์ ์šฉํ•˜๊ฑฐ๋‚˜, ๋จธํ‹ฐ๋ฆฌ์–ผ ์ธ์Šคํ„ด์Šค ๋ฐฐ์—ด ๊ด€๋ฆฌ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.


3. HLSL CBUFFER ๋ฉ”๋ชจ๋ฆฌ ๋ ˆ์ด์•„์›ƒ ์ •๋ ฌ ์ตœ์ ํ™” (Deep-Dive)

SRP Batcher๊ฐ€ ํ˜ธํ™˜๋˜๋”๋ผ๋„, HLSL์˜ CBUFFER ๋‚ด๋ถ€ ๋ฉ”๋ชจ๋ฆฌ ํŒจํ‚น(Memory Packing) ๊ทœ์น™์„ ์ดํ•ดํ•˜์ง€ ๋ชปํ•˜๋ฉด GPU ๋ฉ”๋ชจ๋ฆฌ ์—‘์„ธ์Šค ํšจ์œจ์„ฑ์ด ํฌ๊ฒŒ ์ €ํ•˜๋ฉ๋‹ˆ๋‹ค. DirectX ๋ฐ Vulkan ๋“ฑ ํ˜„๋Œ€ GPU ๊ทธ๋ž˜ํ”ฝ์Šค API๋Š” CBUFFER ๋‚ด์˜ ๋ฐ์ดํ„ฐ๋ฅผ 16๋ฐ”์ดํŠธ(4 components ร— 4 bytes) ๊ฒฝ๊ณ„๋กœ ํŒจํ‚นํ•ฉ๋‹ˆ๋‹ค.

HLSL 16๋ฐ”์ดํŠธ ํŒจํ‚น ๊ทœ์น™ (16-byte Alignment Rule)

HLSL ๋ ˆ์ง€์Šคํ„ฐ(Register)๋Š” 4๊ฐœ์˜ float(x, y, z, w) ํŒจํ‚ค์ง€๋กœ ๋ฉ”๋ชจ๋ฆฌ๋ฅผๅ‰ฒใ‚Šๅฝ“ใฆ(ํ• ๋‹น)ํ•ฉ๋‹ˆ๋‹ค. ํ•˜๋‚˜์˜ ๋ณ€์ˆ˜๊ฐ€ 16๋ฐ”์ดํŠธ ๊ฒฝ๊ณ„๋ฅผ ๊ฑธ์ณ์„œ ๋ฐฐ์น˜๋˜๋Š” ๊ฒƒ์€ ํ—ˆ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ฒฝ๊ณ„๋ฅผ ๋„˜์–ด๊ฐ€๊ฒŒ ๋˜๋ฉด GPU ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ์ž๋™์œผ๋กœ ํŒจ๋”ฉ(Padding) ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์‚ฝ์ž…ํ•˜์—ฌ ์˜คํ”„์…‹์„ ์กฐ์ ˆํ•ฉ๋‹ˆ๋‹ค.

๋ณ€์ˆ˜ ํƒ€์ž… (Data Type) ํฌ๊ธฐ (Size) ์ •๋ ฌ ๋‹จ์œ„ (Alignment)
float / int / uint / bool 4 Bytes 4-byte Boundary
float2 8 Bytes 8-byte Boundary
float3 12 Bytes 16-byte Boundary
float4 / float4x4 16 Bytes (Matrix๋Š” 64 Bytes) 16-byte Boundary

์ž˜๋ชป๋œ ๋น„ํšจ์œจ์  CBUFFER ๋ฐฐ์น˜ ์˜ˆ์‹œ

์•„๋ž˜๋Š” ๋ณ€์ˆ˜ ๋ฐฐ์น˜ ์ˆœ์„œ๊ฐ€ ์—‰๋ง์ด์–ด์„œ ๋ถˆํ•„์š”ํ•œ ํŒจ๋”ฉ ๋ฐ”์ดํŠธ๊ฐ€ ๋Š˜์–ด๋‚˜๊ณ  GPU ๋ Œ๋”๋ง ์„ฑ๋Šฅ์„ ์ €ํ•˜์‹œํ‚ค๋Š” ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค.

// BAD: ๋ฉ”๋ชจ๋ฆฌ ํŒŒํŽธํ™” ๋ฐ ๋ถˆํ•„์š”ํ•œ ํŒจ๋”ฉ ๋ฐœ์ƒ
CBUFFER_START(UnityPerMaterial)
    float _Alpha;          // 4 Bytes (Offset: 0) -> ๋ ˆ์ง€์Šคํ„ฐ 0 (x)
    float3 _BaseColor;     // 12 Bytes (Offset: 16) -> ๋ ˆ์ง€์Šคํ„ฐ 0์˜ yzw์— ๋ชป ๋“ค์–ด๊ฐ! 16๋ฐ”์ดํŠธ ๊ฒฝ๊ณ„ ๋„˜์–ด๊ฐ์— ๋”ฐ๋ผ ๋ ˆ์ง€์Šคํ„ฐ 1๋กœ ์ด๋™ (y,z,w 12๋ฐ”์ดํŠธ ๋ถˆํ•„์š”ํ•œ ๋‚ญ๋น„ ๋ฐœ์ƒ)
    float2 _UVOffset;      // 8 Bytes (Offset: 32) -> ๋ ˆ์ง€์Šคํ„ฐ 2 (xy)
    float _Metallic;       // 4 Bytes (Offset: 40) -> ๋ ˆ์ง€์Šคํ„ฐ 2 (z)
CBUFFER_END

์ตœ์ ํ™”๋œ ์ •๋ ฌ CBUFFER ๊ตฌ์กฐ

ํฌ๊ธฐ๊ฐ€ ํฐ ๋ณ€์ˆ˜(16๋ฐ”์ดํŠธ ๋‹จ์œ„์ธ float4, float4x4)๋ฅผ ์ƒ๋‹จ์— ๋ฐฐ์น˜ํ•˜๊ณ , float3, float2, float ๋‹จ์ผ ๋ณ€์ˆ˜๋“ค์„ ๊ฒฐํ•ฉํ•˜์—ฌ 16๋ฐ”์ดํŠธ ๋ ˆ์ง€์Šคํ„ฐ ํฌ๊ธฐ๋ฅผ ์™„๋ฒฝํ•˜๊ฒŒ ์ถฉ์ „ํ•˜๋„๋ก ์žฌ๋ฐฐ์น˜ํ•ฉ๋‹ˆ๋‹ค.

// GOOD: 16๋ฐ”์ดํŠธ ๋‹จ์œ„ ์™„๋ฒฝ ์ •๋ ฌ (SRP Batcher ๋ฐ GPU ๋ฉ”๋ชจ๋ฆฌ Access ์ตœ์ ํ™”)
CBUFFER_START(UnityPerMaterial)
    float4 _BaseColor;     // 16 Bytes (Register 0: x, y, z, w)
    float2 _UVOffset;      // 8 Bytes  (Register 1: x, y)
    float  _Metallic;      // 4 Bytes  (Register 1: z)
    float  _Alpha;         // 4 Bytes  (Register 1: w) -> ๋ ˆ์ง€์Šคํ„ฐ 1 ์™„๋ฒฝ ์ถฉ์ „(16 Bytes)
CBUFFER_END

4. ์˜ฌ๋ฐ”๋ฅธ HLSL ์ปค์Šคํ…€ ์…ฐ์ด๋” ๋ฆฌํŒฉํ† ๋ง ์‹ค์ „ ์ฝ”๋“œ

์‹ค์ œ URP ํ”„๋กœ์ ํŠธ์—์„œ SRP Batcher ํ˜ธํ™˜์„ฑ์„ ์œ ์ง€ํ•˜๋ฉด์„œ ์ž‘์„ฑํ•˜๋Š” ์ปค์Šคํ…€ HLSL ํŒจ์Šค(Pass)์˜ ํ‘œ์ค€ ํ…œํ”Œ๋ฆฟ ์ฝ”๋“œ ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค.

Shader "Custom/URP_SRPBatcher_Optimized"
{
    Properties
    {
        _BaseColor("Base Color", Color) = (1, 1, 1, 1)
        _BaseMap("Base Map", 2D) = "white" {}
        _Smoothness("Smoothness", Range(0, 1)) = 0.5
        _Metallic("Metallic", Range(0, 1)) = 0.0
    }

    SubShader
    {
        Tags 
        { 
            "RenderType" = "Opaque" 
            "RenderPipeline" = "UniversalPipeline" 
            "Queue" = "Geometry"
        }

        Pass
        {
            Name "ForwardLit"
            Tags { "LightMode" = "UniversalForward" }

            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            // URP ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ (UnityPerDraw CBUFFER ํฌํ•จ)
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

            // ํ…์Šค์ฒ˜ ๋ฐ ์ƒ˜ํ”Œ๋Ÿฌ๋Š” CBUFFER ๋ฐ”๊นฅ์— ์„ ์–ธ
            TEXTURE2D(_BaseMap);
            SAMPLER(sampler_BaseMap);

            // SRP Batcher ํ•ต์‹ฌ: Properties์— ์„ ์–ธ๋œ ๋ชจ๋“  ๋ณ€์ˆ˜๋ฅผ UnityPerMaterial CBUFFER ๋‚ด์— ์ˆ˜๋ก
            // ๋ฉ”๋ชจ๋ฆฌ ์ •๋ ฌ ์ˆœ์„œ: float4 -> float2 -> float
            CBUFFER_START(UnityPerMaterial)
                float4 _BaseColor;
                float4 _BaseMap_ST; // UV Scale & Tiling
                float _Smoothness;
                float _Metallic;
            CBUFFER_END

            struct Attributes
            {
                float4 positionOS   : POSITION;
                float2 uv           : TEXCOORD0;
            };

            struct Varyings
            {
                float4 positionCS   : SV_POSITION;
                float2 uv           : TEXCOORD0;
            };

            Varyings vert(Attributes input)
            {
                Varyings output;
                // TransformObjectToHClip์€ Core.hlsl ๋‚ด UnityPerDraw CBUFFER ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉ
                VertexPositionInputs positionInputs = GetVertexPositionInputs(input.positionOS.xyz);
                output.positionCS = positionInputs.positionCS;
                output.uv = TRANSFORM_TEX(input.uv, _BaseMap);
                return output;
            }

            half4 frag(Varyings input) : SV_Target
            {
                half4 texColor = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv);
                return texColor * _BaseColor;
            }
            ENDHLSL
        }
    }
}


5. ์ฒดํฌ๋ฆฌ์ŠคํŠธ ๋ฐ ์ตœ์ข… ์š”์•ฝ

HLSL ์ปค์Šคํ…€ ์…ฐ์ด๋” ์—ฐ๋™ ํ›„ ๋“œ๋กœ์šฐ ์ฝœ ์ด์Šˆ๋ฅผ ์˜ˆ๋ฐฉํ•˜๊ณ  ์ตœ์ƒ์˜ ๊ทธ๋ž˜ํ”ฝ์Šค ์„ฑ๋Šฅ์„ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ๋ฐฐํฌ ์ „ ๋‹ค์Œ ํ•ญ๋ชฉ์„ ๋ฐ˜๋“œ์‹œ ๊ฒ€์ฆํ•˜์„ธ์š”.

  1. Properties์™€ CBUFFER ์ผ์น˜ ์—ฌ๋ถ€: ์…ฐ์ด๋”์˜ Properties ๋ธ”๋ก์— ์ •์˜ํ•œ ๋ชจ๋“  ๋‹จ์ผ ๋ณ€์ˆ˜(ํ…์Šค์ฒ˜ ํ•ธ๋“ค ์ œ์™ธ)๊ฐ€ CBUFFER_START(UnityPerMaterial) ๋‚ด๋ถ€์— ์ผ์น˜ํ•˜๋Š” ์ด๋ฆ„๊ณผ ํƒ€์ž…์œผ๋กœ ํฌํ•จ๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  2. ํ…์Šค์ฒ˜ ๋ฐ ์ƒ˜ํ”Œ๋Ÿฌ ์œ„์น˜: TEXTURE2D, SAMPLER ๊ตฌ๋ฌธ์€ CBUFFER ๋‚ด๋ถ€์— ๋„ฃ์ง€ ์•Š๊ณ  ์™ธ๋ถ€์— ์„ ์–ธํ–ˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  3. 16๋ฐ”์ดํŠธ ์ •๋ ฌ ๊ทœ์น™ ์ ์šฉ: CBUFFER ๋‚ด๋ถ€ ๋ณ€์ˆ˜๋“ค์„ float4 โ†’ float3/float1 โ†’ float2 ์ˆœ์„œ ๋“ฑ์œผ๋กœ ๋ฐฐ์น˜ํ•˜์—ฌ ๋ฉ”๋ชจ๋ฆฌ ํ™€(Memory Hole) ๋ฐ ๋ถˆํ•„์š”ํ•œ ํŒจ๋”ฉ์„ ์ œ๊ฑฐํ–ˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  4. Frame Debugger ๊ฒ€์ฆ: ๋ Œ๋”๋ง ๋ฃจํ”„ ์‹คํ–‰ ์‹œ RenderLoopDevice.DrawSRPBatcher ๋ฐฐ์น˜ ๊ทธ๋ฃน์— ์ธ์Šคํ„ด์Šค๋“ค์ด ๋ฌธ์ œ์—†์ด ๋ณ‘ํ•ฉ(Batching)๋˜๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

URP ํ™˜๊ฒฝ์—์„œ ์ปค์Šคํ…€ HLSL์„ ๋‹ค๋ฃฐ ๋•Œ SRP Batcher ๊ทœ๊ฒฉ์„ ์ค€์ˆ˜ํ•˜๊ณ  CBUFFER ๋ ˆ์ด์•„์›ƒ์„ ์ •๊ตํ•˜๊ฒŒ ๋‹ค๋“ฌ๋Š” ์ž‘์—…์€ CPU์™€ GPU ์–‘์ชฝ์˜ ๋ณ‘๋ชฉ์„ ์—†์• ๋Š” ๊ฐ€์žฅ ํ™•์‹คํ•œ ๊ธธ์ž…๋‹ˆ๋‹ค. ์œ„ ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… ๊ทœ์น™๊ณผ ์ตœ์ ํ™” ํŒจํ„ด์„ ์‹ค๋ฌด ์ฝ”๋“œ๋ฒ ์ด์Šค์— ๋ฐ˜์˜ํ•˜์—ฌ ์พŒ์ ํ•œ ํ”„๋ ˆ์ž„ ๋ ˆ์ดํŠธ๋ฅผ ํ™•๋ณดํ•ด ๋ณด์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

๋ฐ˜์‘ํ˜•