2025. 2. 20. 20:49ㆍPot of Thoughts/Fragments of Think
위 사진은 GPU에 있는 Scene 데이터를 CPU의 데이터와 동기화 시키기 위해, 변경점들을 Upload Heap에 있는 Staging Buffer에 기록 한 다음, Async Copy Queue(엔진에선 Frame Critical Copy Queue; 현재 Copy Queue를 2개를 활용하여 렌더링에 당장 필요한 데이터를 다루기 위한 Copy Queue와 에셋 업로드 또는 스트리밍등 무거운 작업을 담당하는 Copy Queue(Non Frame Critical Copy Queue)를 따로 분리 하였습니다. 이는 ExecuteCommandLists의 특성, 더 나아가서 GPU에서 명령어를 실행하고 Fence(barrier)에 신호를 주는 방식에 기인한 조치 입니다.)를 통해 데이터를 실제 Scene Data들이 있는 버퍼들로 옮겨 주는 가정에서 각 커맨드들이 어떻게 overlap되고 있는지 PIX를 통해 캡처한 것 입니다.
일단 문제는 현재 엔진의 처리 방식이 워커 스레드 별로 업데이트 할 데이터를 균등하게 분배하고, 업데이트 될 데이터들을 각 스레드 독자적으로 배치(메모리 공간상 연속되어있다면 최대한 하나의 Copy 커맨드로 통합) 하는 형태를 가지고 있습니다. 이 방식을 통해 CPU 타임은 최대한 감소시키면서도 GPU에서 실행될 Copy 커맨드의 수는 최소화 하고자 했습니다.
하지만 당연히 GPU도 명령어를 읽어오고, 적절한 방식으로 스케줄링 해주는 과정을 거쳐야 합니다. 즉 같은 일이면 커맨드 수를 최소화 해준다면 GPU의 스케줄링 부담 역시 줄일 수 있을 것이라고 생각하였습니다.
이 생각을 기반으로, CPU에서 흩어져있던 변경 사항에 대한 정보를 한번 모은 다음(gathering), 이 상태에서 변경 사항들이 메모리 공간 상에서 연속되도록 정렬을 해준 후 각 스레드로 재분배 하여 Copy 커맨드를 생성 해낼 수 있도록 알고리즘을 수정하였습니다.
그 결과, 눈에 뛸 정도로 Copy Command들을 통합 할 수 있었습니다. 실제로 예상대로 GPU 타임도 약 0.235 ms를 감소 시킬 수 있었습니다. 다만 문제는, CPU의 추가적인 작업으로 인해 CPU의 처리시간이 0.7 ms 상승 했다는 점 입니다. 테스트한 환경이 거의 모든 인스턴스의 값들이 변화가 있다는 점을 고려하였을 때, 처리량은 동일하지만 메모리 상의 변경 지점이 연속적이지 않은 경우엔 오히려 CPU의 처리시간만 추가적으로 늘어 날 것이라고 예상 됩니다.
이런 경우 2가지 방법을 선택 할 수 있을 것 이라 생각 됩니다.
1. 기존 알고리즘을 유지하고 해당 위치에 추가적으로 Async Compute 작업을 넣어, 레이턴시를 숨긴다.
2. Scene Data를 들고있는 Buffer를 Default Heap이 아닌 Gpu Upload Heap에 위치 시킨다.
성능적으로 보았을 때, 2번이 가장 좋은 성능을 보여줄 것 이라고 생각 됩니다. 왜냐하면 Gpu Upload Heap은 ReBAR를 통해 MMIO로 직접 VRAM에 PCI-E를 통해 써놓을 수 있기 때문입니다. 기존 알고리즘은 중간에 SysMem에만 존재하는 Staging Buffer(Upload Heap)에 값을 써 놓고, 추가적으로 Upload Heap에서 VRAM에 존재하는 Buffer(Default Heap)으로 복사하는 커맨드를 기록 및 실행(이 부분에서 PCI-E를 통한 데이터 읽기 발생), 거기에 더해 Fence를 통해 값들이 모드 복사 되었음을 보장하기 위한 Fence를 통한 명시적인 동기화를 해야 하기에, 성능적 이점이 발생하는 것은 당연하다고 볼 수 있습니다.
다만 문제는 Gpu Upload Heap을 지원 하기위한 하드웨어적 요건이 아직 까다로운 편에 속한 다는 것입니다. NVIDIA 그래픽 카드를 기준으로 RTX 3000(Ampere Architecture) 이상의 그래픽 카드가 요구 될 뿐 아니라, 메인보드/CPU 또한 PCI-E의 ReBAR를 지원해야만 합니다. 현재 RTX 5000(Ada Architecture)가 이제 막 출시된 시점에서 여전히 신생 기능에 속한다고 볼 수 있습니다.
추가적으로 고려할 점은, Scene Data와 같이 프레임 마다 데이터가 업데이트 될 수 있는 경우, 즉 현재 GPU에서 사용중인 메모리 공간의 데이터를 변경 시킬 수 있기에 이를 방지하기 위해 추가적인 대처가 필요 합니다. 예를 들어, 최대 Inflight Frames 수 만큼의 버퍼를 할당. 이렇게 되면 추가적인 메모리 공간을 사용해야 하기 때문에 주의 할 필요가 있습니다. 하지만 이런 문제는 기존 방식에서 사용된 Staging Buffer의 경우에도 동일하게 적용되는 문제입니다. 즉, 실제로 적용 시 기존 방식보다 극명하게 메모리 공간을 추가로 사용하진 않을 것 이라고 예상됩니다.
'Pot of Thoughts > Fragments of Think' 카테고리의 다른 글
전통적인 그래픽스 파이프라인 vs 메쉬 셰이더 파이프라인(Meshlet Rendering) (0) | 2025.02.18 |
---|---|
Frames In Flight의 개념 (0) | 2025.02.06 |
StaticTransformTag (0) | 2025.02.06 |
언리얼에서 활용한 Skeletal Mesh를 Blender FBX Export를 할 때 (0) | 2024.11.23 |
D3D12 래퍼 설계에 대하여 #2 (0) | 2024.02.08 |