TECHARTNOMAD | TECHARTFLOWIO.COM

GRAPHICS PROGRAMMING

[번역]GPU DRIVEN RENDERING OVERVIE

jplee 2023. 7. 14. 14:16
 

GPU Driven Rendering Overview

Practical guide to vulkan graphics programming

vkguide.dev

  1. GPU Driven Rendering
  2. GPU Driven Rendering Overview

튜토리얼 코드 베이스, 메인 뷰 및 셰도우 뷰에서 처리 및 제거된 객체 수는 125,000개이며, FPS는 290입니다. 이 뷰는 2개의 메시 패스로 인해 40백만 개 이상의 삼각형을 렌더링합니다. RTX 2080에서 렌더링됩니다.
 

GPU Driven Rendering

지난 몇 년 동안 블리딩 엣지 렌더 엔진은 컴퓨팅 셰이더의 GPU에서 렌더링 자체를 계산하는 방향으로 점점 더 많이 움직이고 있습니다. MultiDrawIndirect 및 유사한 기능의 도입 덕분에 이제 컴퓨팅 셰이더 내에서 렌더링 작업을 매우 많이 수행 할 수 있습니다. 이점은 분명합니다.

  • GPU는 데이터 병렬 알고리즘에서 CPU보다 훨씬 높은 성능을 제공합니다. 렌더링은 거의 모든 데이터 병렬 알고리즘입니다.
  • GPU가 자체 작업을 결정하면 CPU에서 GPU로 왕복 하지 않으므로 지연 시간이 최소화됩니다.
  • 이제 다른 작업에 사용할 수 있는 많은 작업에서 CPU를 해제합니다.

결과는 수십 배 또는 그 이상의 장면 복잡성과 객체 수입니다.
5 장 튜토리얼의 끝 부분에 있는 엔진을 기반으로 한 이후 장에서 살펴볼 코드에서는 Nintendo Switch에서 60fps 이상의 속도로 250.000 "drawcalls"를 실행할 수 있습니다. PC에서는 500fps에 도달합니다. 우리는 본질적으로 거의 무제한의 객체 수를 가지고 있으며 병목 현상은 GPU를 그리려는 삼각형 수로 이동합니다.

CPU 처리 시간은 0.5 밀리 초 미만입니다. 위의 보기는 라이젠 1700 이다.
컴퓨팅 셰이더 렌더링을 기반으로하는 기술은 지난 5 년 동안 더욱 인기를 얻고 있습니다. 그 전에는 CAD 유형 장면에서 더 많이 사용되었습니다. 유명하게도 Assassins Creed Unity와 속편은 이러한 기술을 사용하여 훨씬 더 복잡한 장면을 구현합니다. 파리는 많은 건물의 내부를 렌더링하기 때문에 엄청난 양의 객체를 가지고 있습니다.
EA의 Frostbite 엔진은 또한 이러한 기술을 사용하여 Dragon Age Inquisition에서 매우 높은 지오메트리 세부 정보를 제공합니다. 이러한 기술은 Rainbow Six Siege가 파괴 시스템에서 생성 된 수천 개의 동적 잔해 물체를 가질 수있는 이유이기도합니다.
이 기술은 삼각형 처리량에 쉽게 병목 현상이 발생하기 때문에 PS4 및 Xbox One 세대 콘솔에서 매우 인기를 얻었습니다. 따라서 매우 정확한 컬링을 사용하면 성능이 크게 향상됩니다. 언리얼 엔진 4와 유니티는 이러한 기술을 사용하지 않지만 언리얼 엔진 5가 사용하는 것처럼 보입니다.
 

Draw Indirect

아이디어의 핵심은 그래픽 API 에서 간접 그리기(Draw Indirect)지원을 사용하는 것이다. 이러한 기술은 모든 그래픽 API에서 작동하지만 낮은 수준의 메모리 관리 및 컴퓨팅 장벽을 더 잘 제어할 수 있기 때문에 Vulkan 도는 DX12 에서 가장 잘 작동한다. 또한 PS4 및 Xbox one 콘솔에서도 훌륭하게 작동하며 차세대 제품에는 Mesh Shader 및 Raytrace 와 같이 더 많이 사용되는 기능이다.
간접 그리기는 호출 자체가 아닌 GPU 버퍼에서 매개변수를 가져오는 그리기 호출입니다. 간접 그리기를 사용할 때 GPU 버퍼의 위치를 기반으로 그리기를 시작하면 GPU 가 해당 버퍼에서 그리기 명령을 실행 합니다.
Pseudocode:

//normal drawing ---------------
vkCmdDrawIndexed(cmd, object.indexCount, 1 /* instance count */,object.firstIndex, object.vertexOffset, object.ID /* firstInstance */ );

//indirect drawing ---------------

Buffer* drawBuffer = create_buffer(sizeof(VkDrawIndexedIndirectCommand));

//we can immediately enqueue the draw indirect
vkCmdDrawIndexedIndirect(cmd, drawBuffer.buffer, 0 /* offset */, 1 /* drawCount */, sizeof(VkDrawIndexedIndirectCommand));


//we can write the actual draw command at any time we want before VkQueueSubmit(), or from a different thread, or from a compute shader
VkDrawIndexedIndirectCommand* command = map_buffer(drawBuffer);

command->indexCount = object.indexCount;
command->instanceCount = 1;
command->firstIndex = object.firstIndex ;
command->vertexOffset = object.vertexOffset;
command->firstInstance = object.ID;

버퍼에서 매개변수를 가져오기 때문에 Compute Shader를 사용하여 이러한 버퍼에 쓰고 컴퓨팅 셰이더에서 Culling 도는 LOD 선택을 수행할 수 있습니다. 이 방법으로 Culling 을 수행하는 것은 이것이 가장 간단하고 성능이 좋은 방법 중 하나이기 때문이다. GPU 의 성능으로 인해 0.5 밀리초 이내에 100만개 이상의 객체를 Culling 할 것으로 쉽게 기대할 수 있다. 정상적인 장면은 너무 멀리까지 보이는 경우가 별로 없다. Dragon Age 또는 Rainbow Six 와 같은 고급 파이프라인을 사용하는 게임에서는 한 단계 더 나아가 메시에서 개별 삼각형도 Culling 한다. 그들은 Culling 이후 존재하는 삼각형으로 출력 인덱스 버퍼를 작성하고 그것을 그리기 위해 간접 그리기를 하용하여 이를 수행한다.
GPU 기반 렌더러를 디자인 할 때 주요 아이디어는 모든 장면이 GPU에 있어야 한다는 것이다. 4장에서 우리는 Load 된 모든 객체에 대한 행렬을 큰 SSBO에 저장하는 방법을 봤었다. GPU 기반 파이프라인에서는 재료 ID 및 Culling 경계와 같은 더 많은 데이터도 저장하려고 한다. 모든 것이 큰 GPU 버퍼에 저장되는 렌더러가 있고 개체당 PushConstants 또는 설명자 세트를 사용하지 않으면 GPU 기반 렌더러를 사용할 준비가 된 것이다. 튜토리얼에 사용 된 엔진의 디자인은 극한 성능의 Compute 기반 엔진을 위한 Refactoring 에 잘 연결 된 것이다.
GPU에 최대한 많은 것을 담고 싶기 때문에 이 파이프라인은 “Bindless” 기술과 결합하면 매우 잘 mapping 된다. 여기서 Material 당 Description set 를 Binding 하거나 Vertex buffer를 변경할 필요가 없다. Doom Eternal 엔진에서는 Binding 이 없는 상태에서 올이하면 엔진은 프레임당 드로우콜을 거의 수행하지 않는다. 이 가이드에서는 지원이 제한되어 있으므로 bindless 텍스처를 사용하지 않을 것이기 때문에 재질당 1의 draw-indirect 호출을 수행한다. 우리는 여전히 모든 메시를 Large vertex buffer 로 병합하여 그리는 도중 지속적으로 binding 해야 하는 것을 방지 할 것이다. bindless 렌더러를 사용하면 Raytracing 을 훨씬 더 효율적이고 효과적으로 수행할 것이다.
 

Bindless Design

NovusCore Wow 에뮬레이션 연구 프로젝트 World of Warcraft 의 전체대륙은 100+ FPS 에서 10만개 미만의 드로우콜로 렌더링된다. (Infinite draw distance)
GPU 기반 파이프라인은 binding 양이 가능한 한 제한적일 때 가장 장 작동한다.
최상의 시나리오는 BindVertexBuffer, BindIndexBuffer, BindPipeline 및 BindDescriptorSet 호출을 극도로 최소화 하는 것이다.
Bindless 디자인CPU 가 훨씬 적은 작업을 수행해야 하기 때문에 CPU측이 훨씬 더 빠르게 작동하도록 하고 GPU는 각 드로콜이 “더 커짐”에 따라 더 나은 사용률로 인해 더 빨라질 수 있다.
최신 GPU는 정말 크로 ramp up/ramp down 시간이 크므로 장면을 렌더링하는 데 사용하는 드로콜이 적을 수록 좋다.
또한 각 드로콜에 대해 엄청난 양의 작업을 제공할 때 유리함으로 사용량을 최대 100%까지 높일 수 있기 때문입니다.
 
Vertex 및 index buffer 를 bindless 로 이동하려면 일반적으로 mesh 를 매우 큰 buffer 로 병합해서 수행합며 Vertex buffer 및 index buffer 매 쌍당 1개의 buffer 를 갖는 대신 장면의 모든 정점 버퍼에 대해 1개의 버퍼가 있습니다.
렌더링 할 때 드로콜에서 BaseVertex offset 을 사용한다.
 
일부 엔진에서는 파이프라인에서 정점 속성을 완전히 제거하고 대신 정점 셰이더의 버퍼에서 정점 데이터를 가져온다.
그렇게 하면 서로 다른 정점 속성 형식을 사용하더라도 엔진의 모든 드로콜에서 대해 1개의 큰 정점 버퍼를 훨씬 쉽게 유지라 수 있습니다.
 
또한 일부 고급 압축 풀기 / 압축 기술을 사용할 수 있으며 이는 메시 셰이더의 주요 사용 사례입니다.
텍스처를 bindless 로 이동하려면 Texture array를 사용한다. 올바른 확장을 사용하면 SSBO를 사용할 때와 같이 셰이더에서 Texture array의 크기를 제한하지 않을 수 있다. 그런 다음 셰이더의 텍스처에 액세스 할 때 다른 버퍼에서 가져온 인덱스로 텍스처에 액세스 한다. Descriptor indexing 확장을 사용하지 않는 경우에도 Texture array 를 사용할 수 있지만 제한 된 크기가 필요하게 된다. 기기 제한을 확인하여 얼마나 클 수 있는지 확인 해야 함.
 
Material 을 bindless로 만들려면 Material 당 파이프라인 1개를 중지해야한다. 대신 재료 매개변수를 SSBO로 이동하고 ubershader 접근방식을 사용하려고 한다. Doom 엔진에서는 전체 게임에 대한 파이프라인 양이 매우 적다. Doom Eternal 에는 500개 미만의 파이프라인이 있는 반면 UnrealEngine 게임에는 종종 100,000개 이상의 파이프라인이 있다.
 
Uber-shader를 사용하여 고유한 파이프라인의 양을 크게 줄이면 효율성을 크게 높일 수 있는데 VkCmdBindPipeline이 vulkan에서 개체를 그릴 때 가장 비용이 많이 드는 호출 중 하나이기 때문이다.
 
Push constants와 동적 설명자(Dynamic descriptor)를 사용할 수 있지만 “global”이어야 합니다. 카메라 위치와 같은 것에 대해 Push constants를 사용하는 것은 완벽하지만 개체별 호출이므로 개체 ID에 사용할 수 없으며 최대한 많은 개체를 1회에 그리기를 원한다.
 
일반적인 워크플로는 버퍼에 오브젝트를 넣고 호출 할 때마다 binding 할 필요가 없도록 큰 버퍼를 사용하는 것이다. 추가적으로 GPU 에서 이러한 버퍼를 작성할 수도 있다는 것이다. 이는 Dragon Age Inquisition 이 Index 버퍼에 대해 수행했던 작업이다. 여기서 Culling shader 에서 버퍼에 기록하여 보이는 삼각형만 그려지도록 한다.

Links

 

Optimizing the Graphics Pipeline With Compute

With further advancement in the current console cycle, new tricks are being learned to squeeze the maximum performance out of the hardware. This talk will present how the compute power of the console and PC GPUs can be used to improve the triangle...

www.gdcvault.com

 

Rendering 'Rainbow Six | Siege'

Rainbow Six | Siege is based on the first iteration of a new current gen only rendering engine. With massively and procedurally destructible levels, it was important to invest in techniques that allow for better scaling on both CPU and GPU. This...

www.gdcvault.com

 

 

Bindless Graphics Tutorial|NVIDIA

Bindless Graphics Tutorial Want to increase your application performance several fold? Links Introduction Bindless Graphics refers to changes to OpenGL that can enable close to an order of magnitude improvement in the CPU-limitedness of graphics applicatio

www.nvidia.com

 

Overview of Vkguide engine architecture for compute rendering.

여기서 기술 핵심은 튜토리얼의 챕터 5 이후에 직접 구현할 수 있다. 엔진에는 Extra 챕터의 내용도 구현되어 있지만 따라하기 쉬워야한다. 시스템에 대한 자세한 내용은 다음 장에서 살펴보자.
 
첫 번째는 GPU 버퍼의 객체 데이터에 올인하는 것이다. 개체별 Push Constants 가 제거되고 개체별 동적 균일 버퍼가 제거되면 모든 것이 Object Matrix 를 저장하고 셰이더에서 index 를 생성하는 ObjectBuffer로 대체된다.
 
Mesh 가 작동하는 방식도 변경한다. 장면을 로드한 후 큰 정점버퍼를 만들고 전체 맵의 모든 메시를 여기에 채운다. 이렇게 하면 정점 버퍼를 다시 바인딩 하지 않아도 된다.
 
데이터 관리가 완료되면 간접 그리기 자체를 구현 할 수 있다.

Mesh-pass로 렌더링 가능한 개체를 분할한다. 각 mesh pass 는 렌더러에서 특정 패스가 된다. 예제 에서는 2개의 mesh pass가 있다.
 
하나는 Forward rendering 된 메시용이고 다른 하나는 그림자 투사용 입니다. 일부 개체는 한 패스에 등록되지만 다른 패스에는 등록되지 않으며 대부분은 두 패스에 모두 등록됩니다. 개별 개체를 여러 mesh-pass로 분리하여 Render-loop를 크게 단순화하고 더 나은 성능을 제공한다.
 
간접그리지를 관리하는 코드는 RenderScene class 에 있다. 이 class 는 mesh-pass의 모든 개체를 일괄 처리로 정렬한다. batch 는 재질 및 mesh 와 일치하는 개체 집합이다. 각 batch 는 인스턴스 드로잉을 수행하는 하나의 DrawIndirect 호출로 렌더링됩니다. 각 mesh-pass(Forward pass , Shadow pass and etc)에는 렌더링에 사용할 batch array 가 포함된다.
 
기본 ObjectBuffer에서 엔진에 로드된 각 오브젝트당 Culling 경계와 함께 객체 ObjectMatrix를 저장한다.
 
프레임을 시작할 때 각 mesh-pass에 있는 개체를 버퍼에 동기화 한다. 이 버퍼는 ObjectID + BatchID의 배열이 된다.
BatchID는 Mesh-pass 의 batch-array에 대한 index 로 직접 매핑된다. 해당 버퍼를 업로드 하고 동기화하면 Culling 을 수행하는 Compute Shader를 실행한다. 
 
ObjectID + BatchID 쌍의 배열에 있는 모든 개체에 대해 ObjectID를 사용하여 ObjectBuffer 의 개체 데이터에 액세스하고 그것이 보이는지 확인한다. 표시되는 경우 BatchID index를 사용하여 그리기 간접 호출이 포함된 Batch array에 그리기를 삽입하여 인스턴스 수를 늘린다. 또한 각 batch 의 InstanceID 에서 ObjectID 로 매핑되는 간접 버퍼에 이것을 writing 합니다. 
 
완료되면 CPU 측에서 mesh-pass 의 batch 를 반복하고 각각을 순서대로 실행하여 각 batch pipe-line과 material descriptor 를 binding 한다. 그러면 GPU는 개체를 렌더링하기 위해 Culling pass 에서 방금 사용한 매개변수를 사용한다.


엮인 글

 

[번역] Hierarchical-Z map based occlusion culling ( Old Post )

역자의 말. 넷이즈 광저우에 위치한 젠 사업부에 테크아트 라인2 리더(뭔가 중국의 보직이 참...P 와 M 을 겸직하도록 승인이 되서)로 근무 할 때 음양사 차세대 버전 개발팀에서 함께 일 했던 동

techartnomad.tistory.com