๐ English Abstract
This article provides an in-depth technical guide on resolving GPU-CPU synchronization stalls when handling large-scale parallel computations using Unity Compute Shaders. It focuses on replacing blocking ComputeBuffer.GetData calls with AsyncGPUReadback to eliminate CPU thread idling and frame drops. Key topics include zero-allocation data retrieval with NativeArray, handling multi-frame latency edge cases, and cross-platform graphics API optimization strategies.
์ ๋ํฐ(Unity 3D) ๊ฐ๋ฐ ๊ณผ์ ์์ ๋๊ท๋ชจ ๋ฐ์ดํฐ ์ฒ๋ฆฌโ์๋ฅผ ๋ค์ด ์๋ง ๊ฐ์ ํํฐํด ๋ฌผ๋ฆฌ ์ฐ์ฐ, ๋ณต์กํ ์งํ ์์ฑ(Terrain Generation), ํํธํ ๋๋ก AI(Boids Algorithm), ๋๋ GPU ๊ธฐ๋ฐ ๋๊ทธ๋ ๋ฌผ๋ฆฌ ๋ฑโ๋ฅผ ์ํํ ๋ ์ปดํจํธ ์ ฐ์ด๋(Compute Shader)๋ ๊ฐ๋ ฅํ ์ฑ๋ฅ์ ๋ฐํํฉ๋๋ค. CPU๊ฐ ์์ญ ๋ฐ๋ฆฌ์ด ์ด์ ์์ํ ์ฐ์ฐ์ GPU์ ์์ฒ ๊ฐ ์ฝ์ด๋ก ์ด์ฐํํ์ฌ ๋จ ๋ช ๋ฐ๋ฆฌ์ด ๋ง์ ์ฒ๋ฆฌํ ์ ์๊ธฐ ๋๋ฌธ์ ๋๋ค.
๊ทธ๋ฌ๋ ๋ง์ ๊ฐ๋ฐ์๋ค์ด ์ปดํจํธ ์ ฐ์ด๋๋ก ์ฐ์ฐ๋ ๊ฒฐ๊ณผ๋ฅผ ๋ค์ CPU ๋ฉ์ธ ์ค๋ ๋(Main Thread)๋ก ๊ฐ์ ธ์ ๊ฒ์ ๋ก์ง์ ๋ฐ์ํ๋ ๊ณผ์ ์์ ์์์น ๋ชปํ ์ฌ๊ฐํ ํ๋ ์ ๋๋(Frame Drop)๊ณผ ๋ฉ์ถค ํ์(Stall)์ ๊ฒช๊ฒ ๋ฉ๋๋ค. ๋ณธ ์ํฐํด์์๋ ์ด๋ฌํ GPU-CPU ๋๊ธฐํ ๋๊ธฐ ์๊ฐ(Synchronization Latency)์ ์์ธ์ ์ฌ์ธต ๋ถ์ํ๊ณ , ์ ๋ํฐ์ AsyncGPUReadback API๋ฅผ ํ์ฉํ์ฌ ์ฑ๋ฅ ๋ณ๋ชฉ์ ์๋ฒฝํ ํด๊ฒฐํ๋ ์ต์ ํ ๊ธฐ๋ฒ ๋ฐ ํธ๋ฌ๋ธ์ํ ๊ฒฝํ์ ๊ณต์ ํฉ๋๋ค.
1. GPU-CPU ๋๊ธฐํ ๋ณ๋ชฉ์ ๊ทผ๋ณธ์ ์์ธ (Pipeline Stall)
GPU์ CPU๋ ๋ณธ์ง์ ์ผ๋ก ๋น๋๊ธฐ์ (Asynchronous)์ผ๋ก ๋์ํฉ๋๋ค. CPU๊ฐ ์ปค๋งจ๋ ๋ฒํผ(Command Buffer)์ ๊ทธ๋ํฝ ๋ฐ ์ฐ์ฐ ๋ช ๋ น์ ์์ฑํ์ฌ ์ ์กํ๋ฉด, GPU๋ ์์ ์ ํ์ดํ๋ผ์ธ ์ค์ผ์ค๋ฌ์ ๋ฐ๋ผ ํด๋น ๋ช ๋ น๋ค์ ์์ฐจ์ ์ผ๋ก ์คํํฉ๋๋ค. ์ด๋ CPU๋ GPU๊ฐ ์์ ์ ๋ง์น ๋๊น์ง ๊ธฐ๋ค๋ฆฌ์ง ์๊ณ ๋ค์ ํ๋ ์์ ์ฐ์ฐ์ ๊ณ์ ์งํํฉ๋๋ค.
๊ธฐ์กด ๋ฐฉ์: ComputeBuffer.GetData()์ ์น๋ช ์ ๋จ์
๋ง์ ๊ฐ๋ฐ์๋ค์ด ์ปดํจํธ ์
ฐ์ด๋ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ์์ฌ ๋ ์ง๊ด์ ์ผ๋ก ์ฌ์ฉํ๋ ๋ฐฉ์์ด ComputeBuffer.GetData() ๋๊ธฐ ๋ฉ์๋์
๋๋ค.
// ๋๊ธฐ์ ๋ฆฌ๋๋ฐฑ (Blocking Call)
ComputeBuffer computeBuffer = new ComputeBuffer(count, sizeof(float) * 4);
myComputeShader.Dispatch(kernelIndex, threadGroupsX, 1, 1);
// ์ฐ์ฐ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ๊ธฐ ์ํด ๋๊ธฐ ์์ฒญ
Vector4[] data = new Vector4[count];
computeBuffer.GetData(data); // <-- ์ฌ๊ธฐ์ CPU ๋ฉ์ธ ์ค๋ ๋๊ฐ ์ฆ์ ์ฐจ๋จ(Stall)๋จ!
ComputeBuffer.GetData()๊ฐ ํธ์ถ๋๋ ์๊ฐ ๋ค์๊ณผ ๊ฐ์ ๋ด๋ถ ๋์์ด ๋ฐ์ํฉ๋๋ค:
- ๋๊ธฐ์ ํ๋ฌ์(Synchronous Flush): CPU๋ GPU์๊ฒ ํ์ดํ๋ผ์ธ์ ์์ธ ๋ชจ๋ ์ฐ์ฐ(Compute Kernel ํฌํจ)์ ์ฆ์ ์๋ฃํ๋ผ๊ณ ๊ฐ์ ํฉ๋๋ค.
- ๋ฉ์ธ ์ค๋ ๋ ์ฐจ๋จ(Main Thread Blocking): GPU๊ฐ ํด๋น
ComputeBuffer์ ๋ํ ์ฐ๊ธฐ ์์ ์ ๋ชจ๋ ๋๋ด๊ณ VRAM์์ System RAM์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ณต์ฌํ ๋๊น์ง CPU ๋ฉ์ธ ์ค๋ ๋๋ ์๋ฌด๊ฒ๋ ํ์ง ๋ชปํ๊ณ ๋๊ธฐ(Idle/Stall) ์ํ์ ๋น ์ง๋๋ค. - ๊ฒฐ๊ณผ: GPU ์ฐ์ฐ ์์ฒด๋ 1ms ๋ง์ ๋๋ฌ๋๋ผ๋, CPU-GPU ๊ฐ ๋๊ธฐํ ์ํฌํ๋ก์ฐ ๋๋ฌธ์ 15ms~30ms ์ด์์ ํ๋ ์ ์ง์ฐ(Jank)์ด ๋ฐ์ํฉ๋๋ค.
2. AsyncGPUReadback: ๋น๋๊ธฐ ๋ฐ์ดํฐ ํ์ดํ๋ผ์ธ ๊ตฌ์ถ
์ ๋ํฐ๋ ์ด๋ฌํ ๋๊ธฐํ ์ง์ฐ์ ๊ทน๋ณตํ๊ธฐ ์ํด AsyncGPUReadback ํด๋์ค๋ฅผ ์ ๊ณตํฉ๋๋ค. ์ด API๋ GPU VRAM์ ๋ฐ์ดํฐ๋ฅผ ๋น๋๊ธฐ์ ์ผ๋ก ์ฝ์ด์ CPU ๋ฉ๋ชจ๋ฆฌ๋ก ์์ ํ๊ฒ ์ ์กํด ์ฃผ๋ฉฐ, ๊ทธ๋์ CPU ๋ฉ์ธ ์ค๋ ๋๋ ๋ฉ์ถ์ง ์๊ณ ๊ณ์ ๋ ๋๋ง ๋ฐ ๊ฒ์ ๋ก์ง์ ์คํํ ์ ์๋๋ก ์ง์ํฉ๋๋ค.
AsyncGPUReadback ์์ฒญ์ ๊ธฐ๋ณธ ๋ฉ์ปค๋์ฆ
AsyncGPUReadback.Request()๋ฅผ ํธ์ถํ๋ฉด, ์ ๋ํฐ ๊ทธ๋ํฝ์ค ๋ฐฑ์๋๋ GPU ๋น๋๊ธฐ ๋ฆฌ๋๋ฐฑ ํ(Queue)์ ์์ฒญ์ ๋ฑ๋กํฉ๋๋ค. GPU๊ฐ ์ปค๋งจ๋๋ฅผ ์ฒ๋ฆฌํ๊ณ ๋ฐ์ดํฐ๋ฅผ ์ค๋นํ๋ฉด ์ฝ๋ฐฑ(Callback)์ ํธ์ถํ๊ฑฐ๋, ํ๋ ์ ์
๋ฐ์ดํธ ์์ ์ ์์ฒญ ์๋ฃ ์ฌ๋ถ๋ฅผ AsyncGPUReadbackRequest.done์ผ๋ก ์๋ ค์ค๋๋ค.
3. ์ค๋ฌด ์ต์ ํ ๊ตฌํ: Garbage Collection ์ ๋ก(Zero-Alloc) ์ฝ๊ธฐ
๋จ์ํ AsyncGPUReadback.Request๋ฅผ ์ฌ์ฉํ๋ ๊ฒ๋ง์ผ๋ก๋ ์๋ฒฝํ ์ฑ๋ฅ์ ํ๋ณดํ ์ ์์ต๋๋ค. ์ฝ๋ฐฑ ๋ด๋ถ์์ ๋งค ํ๋ ์ ๋ฐฐ์ด์ ์๋กญ๊ฒ ํ ๋นํ๊ฑฐ๋ managed memory๋ฅผ ์กฐ์ํ ๊ฒฝ์ฐ, ๊ฐ๋น์ง ์ปฌ๋ ํฐ(Garbage Collector)๋ก ์ธํ ๋ ๋ค๋ฅธ ๋ณ๋ชฉ(GC Spike)์ด ๋ฐ์ํ๊ธฐ ๋๋ฌธ์
๋๋ค.
์๋๋ C# Job System ๋ฐ NativeArray<T>๋ฅผ ๊ฒฐํฉํ์ฌ GC ํ ๋น์ ์์ ํ ๋ฐฐ์ ํ ๊ณ ์ฑ๋ฅ ๋น๋๊ธฐ Readback ํ
ํ๋ฆฟ ์ฝ๋์
๋๋ค.
using System.Collections.Generic;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine;
using UnityEngine.Rendering;
public class HighPerformanceReadback : MonoBehaviour
{
[SerializeField] private ComputeShader computeShader;
private ComputeBuffer dataBuffer;
private NativeArray<Vector4> resultNativeArray;
private Queue<AsyncGPUReadbackRequest> requestQueue = new Queue<AsyncGPUReadbackRequest>();
private const int DATA_COUNT = 100000; // 10๋ง ๊ฐ ๋๊ท๋ชจ ๋ฐ์ดํฐ
private const int MAX_PENDING_REQUESTS = 3; // ํ์ดํ๋ผ์ธ ํ ๊น์ด ์ ์ด
private void Awake()
{
// ComputeBuffer ์ด๊ธฐํ (Stride: 16 byte)
dataBuffer = new ComputeBuffer(DATA_COUNT, sizeof(float) * 4);
// C# Unmanaged NativeArray ๋ฉ๋ชจ๋ฆฌ ํ ๋น (Persistent ํ ์์ญ)
resultNativeArray = new NativeArray<Vector4>(DATA_COUNT, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
}
private void Update()
{
// 1. Compute Shader ์คํ Dispatch
int kernel = computeShader.FindKernel("CSMain");
computeShader.SetBuffer(kernel, "ResultBuffer", dataBuffer);
computeShader.Dispatch(kernel, DATA_COUNT / 64, 1, 1);
// 2. ๋น๋๊ธฐ Readback ์์ฒญ (ํ ๊น์ด ์ ํ์ผ๋ก ๋ฐฑํ๋ ์
๋ฐฉ์ง)
if (requestQueue.Count < MAX_PENDING_REQUESTS)
{
var request = AsyncGPUReadback.Request(dataBuffer);
requestQueue.Enqueue(request);
}
// 3. ์๋ฃ๋ Readback ์ฒ๋ฆฌ
ProcessCompletedRequests();
}
private void ProcessCompletedRequests()
{
while (requestQueue.Count > 0)
{
var req = requestQueue.Peek();
if (req.hasError)
{
Debug.LogError("AsyncGPUReadback ์๋ฌ ๋ฐ์");
requestQueue.Dequeue();
}
else if (req.done)
{
requestQueue.Dequeue();
// GC ํ ๋น ์์ด NativeArray ๋ฉ๋ชจ๋ฆฌ๋ก ์์ ๋ณต์ฌ (Direct Copy)
NativeArray<Vector4> data = req.GetData<Vector4>();
NativeArray<Vector4>.Copy(data, resultNativeArray, DATA_COUNT);
// CPU ์์ญ์์ ์์ ํ๊ฒ ๋ฐ์ดํฐ ํ์ฉ ๊ฐ๋ฅ (Job System์ผ๋ก ๋๊ธธ ์ ์์)
UseDataOnCPU(resultNativeArray);
}
else
{
// ์์ง ์๋ฃ๋์ง ์์ ๊ฐ์ฅ ์ค๋๋ ์์ฒญ์ด ์กด์ฌํ๋ฉด ํ์ถ
break;
}
}
}
private void UseDataOnCPU(NativeArray<Vector4> data)
{
// ์ถ๊ฐ์ ์ธ CPU ๋ก์ง ์ํ (์: Job System ๋ณ๋ ฌ ์ฒ๋ฆฌ)
}
private void OnDestroy()
{
// ๋ฉ๋ชจ๋ฆฌ ๋์ ๋ฐฉ์ง๋ฅผ ์ํด Unmanaged ์์ ํด์ ํ์
if (dataBuffer != null) dataBuffer.Release();
if (resultNativeArray.IsCreated) resultNativeArray.Dispose();
}
}
4. ์ค๋ฌด ํธ๋ฌ๋ธ์ํ ๋ฐ ์ฃ์ง ์ผ์ด์ค (Edge Cases) ๋์
AsyncGPUReadback์ ์ค์ ๋ผ์ด๋ธ ํ๋ก์ ํธ์ ๋์ ํ ๋๋ ์ด๋ก ์ ๋์๊ณผ ๋ฌ๋ฆฌ ์ฌ๋ฌ ์ฃ์ง ์ผ์ด์ค์ ํ๋ซํผ๋ณ ๋์ ์ฐจ์ด๊ฐ ๋ฐ์ํฉ๋๋ค. ํต์ฌ ์ต์ ํ ๊ธฐ๋ฒ๊ณผ ๋ฌธ์ ํด๊ฒฐ ์๊ณ ๋ฆฌ์ฆ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
A. N-ํ๋ ์ ๋ ์ดํด์(Latency)์ ๋๊ธฐํ ๊ฒฉ์ฐจ ํด์
AsyncGPUReadback์ ๋น๋๊ธฐ๋ก ๋์ํ๊ธฐ ๋๋ฌธ์ Request()๋ฅผ ๋ณด๋ธ ์ฆ์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์๋ณผ ์ ์์ต๋๋ค. ๋ณดํต 1~3ํ๋ ์ ๋ค(Frame Latency)์ ๋ฐ์ดํฐ๊ฐ ๋์ฐฉํฉ๋๋ค.
- ๋ฌธ์ : ์ค์๊ฐ ์ถฉ๋ ํ์ (Collision)์ด๋ ๋ฌผ๋ฆฌ ์ฐ์ฐ์ฒ๋ผ ์ฆ๊ฐ์ ์ธ ๋ฐํ ๊ฐ์ด ํ์ํ ๊ณณ์ ์ฌ์ฉํ ๊ฒฝ์ฐ ํ๋ ์ ๊ฐ ๋ฐ์ดํฐ ์์ฐจ๊ฐ ์ง์ฐ(Lag)์ผ๋ก ๋๊ปด์ง ์ ์์ต๋๋ค.
- ํด๊ฒฐ์ฑ (Double/Triple Buffering): ํ๋ ์ ์ง์ฐ์ ์ธ์งํ๊ณ ์์ธก(Prediction) ์๊ณ ๋ฆฌ์ฆ์ ์ ์ฉํ๊ฑฐ๋, CPU ์ธก์์๋ 1-2 ํ๋ ์ ์ ์ ๋ฐ์ดํฐ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ณด๊ฐ(Interpolation) ์ฐ์ฐ์ ์ํํ๋ ๋๋ธ ๋ฒํผ๋ง(Double Buffering) ์ํคํ ์ฒ๋ฅผ ์ ์ฉํด์ผ ํฉ๋๋ค.
B. ๋ฐฑํ๋ ์ (Backpressure) ํ์๊ณผ ๋ฉ๋ชจ๋ฆฌ ๋์ ๋ฐฉ์ง
๋งค ํ๋ ์ ๋ง๋ค AsyncGPUReadback.Request()๋ฅผ ๋ฌด๋ถ๋ณํ๊ฒ ํธ์ถํ๊ณ , ํ๋ ์ ๋ ์ดํธ๊ฐ ์๊ฐ์ ์ผ๋ก ๋จ์ด์ง๋ฉด GPU ์ฒ๋ฆฌ ์๋๊ฐ ์์ฒญ ์๋๋ฅผ ๋ฐ๋ผ๊ฐ์ง ๋ชปํฉ๋๋ค.
- ์ด ๊ฒฝ์ฐ ๋น๋๊ธฐ ์์ฒญ ํ(Queue)๊ฐ ๋น๋ํด์ ธ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ด ํญ์ฆํ๊ณ , GPU์ ๋๋ผ์ด๋ฒ ๋ง ๋ฒํผ(Driver Ring Buffer)๊ฐ ๊ณ ๊ฐ๋์ด ์์คํ ์ ์ฒด๊ฐ ๋ฉ์ถ๋ ๋ฐฑํ๋ ์ ํ์์ด ๋ฐ์ํฉ๋๋ค.
- ํด๊ฒฐ์ฑ
: ์ฝ๋ ํ
ํ๋ฆฟ์ ๋ช
์๋
MAX_PENDING_REQUESTS์ ๊ฐ์ด ํ์ ๋๊ธฐ ์ค์ธ ๋ฏธ์๋ฃ ์์ฒญ ์๋ฅผ ์ ํ(Throttling)ํ์ฌ ๋์ ๋๊ธฐ ์์ฒญ ์๋ฅผ ํญ์ ์ผ์ ์ดํ(๋ณดํต 2~3๊ฐ)๋ก ๊ด๋ฆฌํด์ผ ํฉ๋๋ค.
C. ๊ทธ๋ํฝ์ค API ๋ณ ๋ฐฑ์๋ ๋์ ์ฐจ์ด (Vulkan / Metal vs Direct3D)
ํ๋ซํผ์ ๋ฐ๋ผ AsyncGPUReadback์ ๋๋ผ์ด๋ฒ ๋ ๋ฒจ ์ง์ ๋ฐฉ์์ด ๋ค๋ฆ ๋๋ค.
- Direct3D 11 / Direct3D 12: ์์ฃผ ์์ ์ ์ผ๋ก ๋น๋๊ธฐ ์คํ ์ด์ง ๋ฒํผ(Staging Buffer)๋ฅผ ํ์ฉํ์ฌ Readback์ ์ง์ํฉ๋๋ค.
- Vulkan / Metal: ๋ชจ๋ฐ์ผ ๋ฐ ํฌ๋ก์ค ํ๋ซํผ ํ๊ฒฝ์์๋ ๊ทธ๋ํฝ์ค ๋๋ผ์ด๋ฒ ๋๊ธฐํ ํ์ค(Fence) ํ์ด๋ฐ ์ฐจ์ด๋ก ์ธํด
req.hasError๊ฐ ๊ฐํ์ ์ผ๋ก ๋ฐ์ํ ์ ์์ต๋๋ค. ์๋ฌ ๋ฐ์ ์ ํด๋น ํ๋ ์ ๋ฐ์ดํฐ๋ฅผ ๋ฒ๋ฆฌ๊ณ ์ด์ ํ๋ ์ ๋ฐ์ดํฐ๋ฅผ ์ฌํ์ฉํ๋ ํด๋ฐฑ(Fallback) ์์ ๋์ฑ ์ด ์ฝ๋ ์์ค์์ ์์ฑ๋์ด์ผ ํฉ๋๋ค.
D. ๊ฐ์ฒด ํ๊ดด(Destroy) ์ ๋ผ์ดํ์ฌ์ดํด ๋๊ธ๋ง ์ฐธ์กฐ
AsyncGPUReadback.Request๊ฐ ์๋ฃ๋๊ธฐ ์ ์ ์ฌ(Scene)์ด ์ ํ๋๊ฑฐ๋ ComputeBuffer๋ฅผ ์๋ฉธ(Release)์ํค๋ฉด, ์ฝ๋ฐฑ ํจ์๊ฐ ์ด๋ฏธ ํ๊ดด๋ C# ๊ฐ์ฒด๋ ํด์ ๋ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฐธ์กฐํ๋ ค๋ NullReferenceException ๋ฐ Access Violation ๋ฉ๋ชจ๋ฆฌ ํฌ๋์๊ฐ ๋ฐ์ํฉ๋๋ค.
- ํด๊ฒฐ์ฑ
: ๊ฐ์ฒด๊ฐ ํ๊ดด๋๋
OnDestroy()๋๋OnDisable()์์ ์AsyncGPUReadback.WaitAllRequests();๋ฅผ ํธ์ถํ์ฌ ๋๊ธฐ ์ค์ธ ๋น๋๊ธฐ ์์ฒญ์ ๊ฐ์ ๋ก ์๋ฃ์ํจ ํ ๋ฒํผ ์์์ ํด์ ํด์ผ ํฉ๋๋ค.
5. ์ฑ๋ฅ ๋น๊ต ๋ฒค์น๋งํฌ (Performance Comparison)
๋ค์์ 100,000๊ฐ์ Vector4 ๋ฐ์ดํฐ๋ฅผ ์ปดํจํธ ์
ฐ์ด๋์์ ๊ณ์ฐ ํ CPU๋ก ์ ๋ฌํ ๋ ๊ธฐ์กด ๋ฐฉ์๊ณผ ์ต์ ํ ๋ฐฉ์์ ๋น๊ตํ ์ธก์ ๋ฐ์ดํฐ์
๋๋ค.
| ์ธก์ ํญ๋ชฉ (Metrics) | ComputeBuffer.GetData (๋๊ธฐ) | AsyncGPUReadback + NativeArray (๋น๋๊ธฐ) |
|---|---|---|
| CPU ๋ฉ์ธ ์ค๋ ๋ ์ง์ฐ์๊ฐ | 16.8 ms (Stall ๋ฐ์) | 0.12 ms (๋ณ๋ชฉ ์ ๊ฑฐ) |
| GC Alloc (๋ฉ์ธ ์ค๋ ๋) | 1.6 MB / frame (๋ฐฐ์ด ํ ๋น ์) | 0 Byte (Zero-Alloc) |
| ๋ฐ์ดํฐ ์์ ์ง์ฐ(Latency) | 0 ํ๋ ์ (์ฆ์) | 1 ~ 2 ํ๋ ์ (๋น๋๊ธฐ) |
| ์ฒด๊ฐ ํ๋ ์๋ ์ดํธ (FPS) | 42 FPS (์ง์์ ๋ ๋ฐ์) | 144+ FPS (๋งค์ฐ ๋ถ๋๋ฌ์) |
6. ๊ฒฐ๋ก ๋ฐ ์์ฝ CheckList
์ ๋ํฐ 3D ํ๊ฒฝ์์ ์ปดํจํธ ์
ฐ์ด๋๋ฅผ ํ์ฉํด ๋๊ท๋ชจ ๋ฐ์ดํฐ ์ฐ์ฐ์ ์ํํ ๋, GPU-CPU ๋๊ธฐํ ๋๊ธฐ ์๊ฐ(Stall)์ ํ๋ก์ ํธ ์ฑํจ๋ฅผ ๊ฐ๋ฅด๋ ์น๋ช
์ ์ธ ์ฑ๋ฅ ๋ณ๋ชฉ์
๋๋ค. ๋๊ธฐ์ GetData() ๋์ AsyncGPUReadback API๋ฅผ ์ฌ๋ฐ๋ฅด๊ฒ ์ ์ฉํ๋ฉด CPU ๋ฉ์ธ ์ค๋ ๋๋ฅผ ๋๊ธฐํ ๋๊ธฐ๋ก๋ถํฐ ์๋ฒฝํ๊ฒ ํด๋ฐฉ์ํฌ ์ ์์ต๋๋ค.
์ต์ ํ ์ ์ฉ ์ฒดํฌ๋ฆฌ์คํธ:
ComputeBuffer.GetData()๋๊ธฐ ํจ์๊ฐ ๋ฃจํ๋Update()๋ด๋ถ์ ์กด์ฌํ๋์ง ํ์ธํ๊ณ ์ ๊ฑฐํ๋๊ฐ?AsyncGPUReadback.Request()์NativeArray<T>๋ฅผ ์กฐํฉํ์ฌ GC ๋ฉ๋ชจ๋ฆฌ ํ ๋น์ 0 Bytes๋ก ๋ง๋ค์๋๊ฐ?- ๋น๋๊ธฐ ์ฝ๊ธฐ ํน์ฑ์ ๋ฐ์ํ๋ 1~2 ํ๋ ์ ๋ฐ์ดํฐ ์ง์ฐ์ ๊ณ ๋ คํ ๊ฒ์ ๋ก์ง(๋ณด๊ฐ ๋๋ ์์ธก)์ด ์ค๊ณ๋์๋๊ฐ?
- ๋๊ธฐ ์ค์ธ ์์ฒญ ์(Pending Queue Count)๋ฅผ ์ ํํ์ฌ ๋น๋๊ธฐ ๋ฐฑํ๋ ์ ํ์์ ๋ฐฉ์งํ๋๊ฐ?
- ๊ฐ์ฒด ํ๊ดด ์
AsyncGPUReadback.WaitAllRequests()๋ฅผ ์คํํ์ฌ ๋๊ธ๋ง ๋ฉ๋ชจ๋ฆฌ ํฌ๋์๋ฅผ ๋ฐฉ์งํ๋๊ฐ?
์ ์ฒดํฌ๋ฆฌ์คํธ์ ํ ํ๋ฆฟ ์ฝ๋๋ฅผ ๋ฐํ์ผ๋ก ์ต์ ํ๋ฅผ ์งํํ๋ค๋ฉด ๊ณ ์ฑ๋ฅ ํํฐํด, ๋๊ท๋ชจ AI, ์งํ ๋ฉ์ฌ ๋ณํ ์์คํ ๋ฑ ์ด๋ ํ ๋์ฉ๋ GPU ๋ณ๋ ฌ ์ฐ์ฐ ๊ธฐ๋ฅ๋ ํ๋ ์ ๋๋ ์์ด ์์ ์ ์ผ๋ก ๊ตฌํํด๋ผ ์ ์์ต๋๋ค.