z Tailor Your Apps for Apple GPUs and Tile-Based Deferred Rendering :: C++, 그래픽 기술 블로그

Overview

애플 실리콘의 GPU는 성능과 전력 효율을 최적화 하기 위해서 Tile-Based Deferred Rendering(이하 TBDR)이라는 렌더링 기술을 사용합니다. 이의 기본이 되는 Tile-Based Rendering(이하 TBR)은 ARM의 Mali에서도 사용되며, 적은 발열 및 전력으로 인해 메모리 대역(Bandwidth)이 적은 모바일에 최적화된 모습을 보이는데, 이러한 형태가 전통적인 PC와 콘솔에서 사용하는 Immediate Mode(이하 IM) GPU와 어떻게 다른 지에 대해서 기술합니다. 추후, 각 타일의 렌더링 단계를 해당 타일의 모든 geometry가 계산될 때까지 연기한 후 실행하여 좀 더 효율적으로 수행하는 Deferred Rendering과 동일합니다. (Deferred Rendering은 나중에 따로 기술하도록 하겠습니다.)

 

Immediate Mode GPUs

 IM이란 primitive마다 strict command stream으로 vertex shader와 fragment shader를 순서대로 수행하는 것을 의미합니다.

 

pseudo-code 상으로는 다음과 같이 표현되며 아래의 그림처럼 도식화됩니다.

python
for draw in renderPass:
    for primitive in draw:
        for vertex in primitive:
            execute_vertex_shader(vertex)
        if primitive not culled:
            for fragment in primitive:
                execute_fragment_shader(fragment)

 

 

Advantages

Vertex Shader 및 geometry에 관련된 shader가 GPU 내부에 On-chip 메모리에 남아 있기에 바로 다음 단계로 넘어가기 위한 FIFO 버퍼에 저장될 수 있습니다. 이는 GPU가 결과값을 저장 및 로딩하는데 필요한 외부 메모리 대역 사용량이 적습니다.

Disadvantages

Fragment Shader에서 결과값을 프레임 버퍼에 그려야하는 경우 문제가 발생하는데, 1440p 해상도의 화면을 생각하면 대략 30MB의 메모리가 필요하며 이는 on-Chip으로 유지되기 힘들기 때문에 모바일의 경우 off-Chip인 DRAM에 보관되어야만 합니다. 이는 Blending, Depth Testing, Stencil Testing과 같은 연산작업은 중간 결과값(Working set)을 사용하게 되는데 읽고 쓰는 작업을 하게 되면 메모리 대역 사용량이 늘어나 발열 및 전력 소모가 커져 모바일에서 문제가 될 가능성이 높습니다.GPU는 매 블랜딩, 깊이 테스트, 스텐실 연산 마다 이 작업세트로 부터 현재 프래그먼트의 픽셀에 대한 현재 데이터를 가져옵니다. 캐싱으로 개선할 수 있는 부분은 존재합니다.

 

Tile-Based GPUs

 TBR은 렌더링 대상을 타일이라고 하는 더 작은 영역의 그리드로 분할하여, GPU 코어 중 하나가 각 타일을 처리하게 하여 GPU가 외부 메모리 접근을 최소화하기에 메모리 대역 사용량을 줄이는데 초점을 맞춥니다. 이때 타일에 따라 Fragment Shader를 수행하기 때문에 크게 RenderPass 처리가 두번 수행하게 됩니다.

  1. 모든 geometry 처리를 수행하여, primitive가 어떤 타일에 해당하는 지에 대한 리스트를 생성합니다.
  2. 각각의 타일에 존재하는 primitive에 대해 Fragment Shader를 수행하여 이를 다시 메모리에 저장합니다.

이는 아래와 같은 pseudo-code가 되며 아래의 그림처럼 도식화됩니다.

python
# Pass one
for draw in renderPass:
    for primitive in draw:
        for vertex in primitive:
            execute_vertex_shader(vertex)
        if primitive not culled:
            append_tile_list(primitive)

# Pass two
for tile in renderPass:
    for primitive in tile:
        for fragment in primitive:
            execute_fragment_shader(fragment)

 

 

 

Advantage: Bandwidth

 가장 큰 장점은 메모리 대역을 적게 사용하는 것으로, Fragment Shader 연산에서 메모리를 가져와 수정하는 연산을 적은 메모리를 차지하는 타일에서 수행하기에 on-chip 메모리에서 행해져 외부 메모리 접근을 필요로 하지 않고 수행될 수 있습니다. 이는 Fragment Shader에서의 연산이 클 수록 큰 이점을 지닙니다.

 또한, 타일 단위의 연산 결과가 이전과 다르지 않다면 결과를 Update하지 않아도 되기에 메모리 대역이 더더욱 줄어듭니다. 하지만 이는 연산 자체는 모두 수행해야하므로, 성능에는 큰 영향을 주지는 않지만 전력 소모를 줄일 수는 있습니다.

Advantage: Algorithms

계산이 비싸거나 메모리 대역이 큰 다음과 같은 알고리즘이 사용 가능해집니다. 특히 알고리즘 특성상 RenderPass를 geometry 단계 처리와 Fragment Shading 처리가 나뉘어지는 경우, 더더욱 적합합니다.

  • Multi-Sample Anti-Aliasing (MSAA)
    MSAA 연산을 위한 추가적인 RenderPass가 필요로 하는데, TBR의 경우는 on-Chip 메모리에 미리 데이터가 존재하므로, 적은 성능 부하와 메모리 대역으로 해결 가능합니다.
  • Deferred Lighting
    타일 단위로 연산을 수행함으로 필요한 데이터 값을 On-chip 메모리에 저장해두었기에, 기존 Multiple Render Target(MRT) 렌더링으로 구현하는 단계가 생략 가능합니다.

Advantage: Efficiency

IM GPU의 경우, 보이는 여부와 관계 없이 모든 Primitive를 처리하는 반면, TBDR GPU의 경우는 geometry 과정을 모두 처리한 이후에 Fragment Shader로 진입하기 때문에, 보이지 않는 primitive를 사용하지 않아 더욱 효과적으로 이용 가능할 수도 있습니다.

 또한, GPU가 두번째 단계의 RenderPass를 수행하고 있을 때, 다음 장면을 위한 첫번째 단계의 RenderPass를 미리 병렬적으로 수행할 수도 있습니다.

Disadvantages

 가장 큰 단점은 geometry 처리 이후의 정점에 대한 결과를 off-Chip 메모리에 저장하여야 하기에, 상황에 따라 TBR이 IM보다 더 큰 메모리 대역을 사용할 수도 있습니다. 특히, Tesselation과 같은 경우는 이를 심화시킬 수도 있습니다.

 


 지금까지 보편적인 TBR의 이야기를 하였다면, 애플 실리콘의 영역으로 넘어와 어떻게 실제적으로 TBDR를 설정하여 사용할 수 있는 지에 대해 알아보겠습니다.

Add Customizations to Your Fragment Shaders with Imageblocks

 사용자는 GPU에서 픽셀 단위의 높은 대역폭 메모리 영역의 사용자 지정 데이터 구조를 정의 및 조작할 수 있습니다. 이미지 블록은 Appl GPU가 효과적으로 다룰 수 있도록 이미지 데이터를 설명하도록 하는 로컬 메모리에 저장된 구조화된 이미지 데이터의 타일입니다. 이는 fragment 과정 및 타일 shading 과정에 깊게 연관되어 있으며, 계산 커널에서도 사용할 수 있습니다. 이전에는 장치 상의 이미지 블록에 렌더링되었지만 A11 GPU를 기점으로 이미지 블록의 데이터 구조를 완벽하게 제어할 수 있게 되었습니다. 이미지 블록은 렌더 패스의 fragment 단계와 타일 단계 사이에서 데이터를 전달할 수 있으며, 스레드 그룹 메모리는 비정형 데이터가 적합하지만 이미지 블록은 이미지 데이터에 더 적합합니다.

이미지 블록은 폭, 높이 및 픽셀의 깊이를 가지고 있는 2D 데이터 구조로, 안의 각각의 픽셀은 다수의 요소로 구성되는데 보편적으로 Albedo, Specular, Normal와 같은 정보를 담고 있습니다.

 이미지 블록은 커널과 fragment 함수에서 모두에서 사용 가능하며, 그리고 보내는 동안 유지되기에 단일 렌더링 패스에서 동일한 로컬 메모리인 타일 셰이더에 대한 렌더링과 연산 작업을 혼합하여 사용할 수 있습니다. 이를 통해 로컬 GPU 메모리 내의 타일 내에서 복잡한 알고리즘 연산을 수행할 수 있도록 합니다.
 기존 코드는 자동으로 렌더 첨부 파일 형식과 일치하는 이미지 블록을 만듭니다. 그러나 셰이더 내에서 자신의 이미지 블록을 완전히 정의할 수도 있습니다. 정의하는 이미지 블록은 렌더 첨부 파일이 만드는 것보다 훨씬 더 복잡할 수 있습니다. 예를 들어, 이미지 블록에는 추가 채널, 배열 및 중첩 구조가 포함될 수 있습니다. 또한 정의한 이미지 블록을 계산의 여러 단계에 걸쳐 다양한 용도로 재사용할 수 있습니다. 
프래그먼트 셰이더 내에서 현재 프래그먼트는 타일 내 해당 프래그먼트의 위치와 연관된 이미지 블록 데이터에만 액세스할 수 있습니다. 계산 기능에서 스레드는 모든 이미지 블록 데이터에 액세스할 수 있습니다. 첨부 파일을 사용하여 렌더링할 때 로드 및 저장 작업은 타일 메모리의 데이터를 읽고 씁니다. 그러나 명시적 이미지 블록을 사용하는 경우 계산 함수를 사용하여 장치 메모리를 명시적으로 읽고 쓰십시오. GPU는 메모리 하드웨어를 활용하는 효율적인 블록 전송에서 타일 메모리의 콘텐츠를 시스템 메모리에 자동으로 플러시할 수 있다.

 

 

Your existing code automatically creates imageblocks that match your render attachment formats. However, you can also define your own imageblocks completely within your shader. Imageblocks that you define can be far more sophisticated than those render attachments create. For example, an imageblock can include additional channels, arrays, and nested structures. Furthermore, you can reuse the imageblocks you define for different purposes across different phases of your computation. 

Within a fragment shader, the current fragment has access to only the imageblock data associated with that fragment’s position in the tile. In a compute function, a thread can access all of the imageblock data. When rendering with attachments, load and store actions still read and write data in tile memory. However, if you’re using explicit imageblocks, use a compute function to explicitly read and write to device memory. The GPU may automatically flush the contents of tile memory to system memory in an efficient block transfer that takes advantage of memory hardware.

Save Memory Bandwidth Across Render and Compute Passes with Tile Shaders

 이는 위에서도 설명되었지만, 타일 셰이딩은 공유 로컬 메모리에서 단일 렌더 패스를 통하여 렌더링과 연산 작업을 혼합하여 사용 가능합니다. 많은 렌더링 기술들이 연산하고 그리는 작업을 필요로 하는데, 일반적인 GPU의 경우는 개별의 렌더 패스에서 이루어져야 하며, 서로 상호작용할 수 없어, 한 렌더 패스의 결과를 장치 메모리에 저장한 후 다른 렌더 패스에서 다시 받아 쓰게 됩니다.

타일 세이더의 경우는 렌더링과 연산이 한 렌더 패스에서 일어나기 떄문에 결과값을 GPU 내의 타일 메모리에 지속적으로 보관하여 이를 장치 메모리로 넘길 필요가 없어 더 빠르게 작업이 수행가능합니다.

 

Sequence Operations with Raster Order Groups

같은 픽셀 좌표에 접근 하는 병렬 fragment 셰이더 스레드를 raster order groups을 통해 정밀하게 제어할 수 있습니다. Raster order groups은 fragment 셰이더로 부터 정렬된 메모리 접근을 제공하며 순서에 독립적인 투명도, 이중 레이어 geometry 버퍼 및 복셀화와 같은 렌더링 기술을 단순화할 수 있습니다. Metal은 GPU 드로우 호출 순서를 혼합되도록 보장하여, GPU가 장면을 순차적으로 ㄹ네더링하는 듯한 착각을 줍니다. 아래와 같은 두 삼각형이 겹치는 장면을 생각하면 겹치는 부분에 대한 블렌딩을 수행해주어야 합니다.

각 삼각형의 fragment 셰이더는 동시적으로 각각의 스레드에서 수행되는데, 블렌딩 함수를 수행하는 문제에 있어 순서가 문제가 될 수 있기에, 앞에서 뒤로 순서대로 각 삼각형의 fragment 셰이더가 수행되어야만 합니다. 따라서 아무런 조치를 취하지 않는다면 동시성에 의해 읽고 수정하여 쓰는 작업은 경쟁 상태를 만들 수 있습니다.

Raster order groups은 같은 픽셀 좌표 및 샘플을 동기화하는 것으로 이러한 문제를 해결하였습니다. Raster order groups를 구현하려면 속성 한정자를 사용하여 메모리에 대한 포인터에 표시해야 합니다. 하드웨어는 현재 스레드가 진행되기 전에 현재 스레드와 겹치는 이전 fragment 셰이더 스레드가 완료될 때까지 기다립니다.

최근의 Apple GPU는 raster order groups에 추가적인 기능을 추가하여, 이미지 블록과 스레드 그룹 메모리에 대한 개별적인 채널을 동기화할 수 있도록 하였습니다. 이는 다수의 order groups을 만들어 보다 세분화된 동기화를 제공하고 접근을 기다리는 스레드를 최소화할 수 있습니다. 만약 TBDR을 사용하지 않고서 deferred shading을 수행할 때, 아래와 같이 두 개의 단계를 필요로 합니다.

  1. G-buffer를 채우고 다수의 텍스쳐를 생성합니다.
  2. 생성한 텍스쳐에 G-buffer와 광원으로 렌더링을 수행합니다.

Deferred sharing는 셰이더가 첫 번째 단계의 셰이더에서 텍스쳐를 장치 메모리에 쓰도록 하고 이를 두 번째 단계에서 읽어와야 하기 때문에 메모리 대역이 집약적으로 사용됩니다. 이는 다수의 order groups를 사용하여 두 단계를 하나로 병합할 수 있어 장치 메모리를 거치는 단계를 생략할 수 있습니다. 이를 위해 G-buffer를 타일 크기의 덩어리를 만들어 이를 이미지 블록 메모리로서 GPU에 남아 있을 수 있도록 합니다. 전통적인 GPU에서는 하나의 픽셀에 대해서 선행한 작업이 완료될 때까지 기다려 충돌을 미연에 방지하지만 강제적으로 순차적으로 수행되게 됩니다.

하지만 다수의 order groups를 사용하면 충돌하지 않고서도 동시적으로 이를 수행할 수 있는데, 먼저 첫 번째 그룹에는 G-buffer 영역을 추가하고 두 번쨰 그룹에는 빛에 조명한 결과를 축적시키도록 합니다. 따라서, 쓰는 작업의 동기화가 읽는 작업과 무관하여 읽고 계산까지의 작업의 효율성을 높입니다. 마지막의 빛의 결과의 축적은 동기적으로 수행되게 됩니다.

Customize Pixel Blending with Enhanced Multisample Antialiasing

셰이더 내의 다중 샘플 추적 데이터에 접근하는 multisample antialiasing (MSAA)를 생성할 수 있습니다. MSAA는 픽셀마다 여러 개의 깊이와 색상 샘플을 사용하여 primitive의 가장자리를 개선하는 기술인데 이는 픽셀 내의 서로 다른 위치에서 각 픽셀을 샘플링하여 들쭉날쭉한 가장자리의 모양을 부드럽게 만들 수 있습니다.

A 시리즈 GPU는 효율적인 MSAA 구현을 가지고 있습니다. 하드웨어는 각 픽셀이 가장자리에 해당하는 지를 확인한 후 필요한 경우에만 작업을 수행합니다. Apple GPU는 각 픽셀에 대해 고유한 샘플의 수를 추적하고 새로운 primitive를 렌더링할 때 해당 데이터를 업데이트합니다.

 

출처

https://mkblog.co.kr/gpu-tile-based-rendering-mobile-gpu/

https://scahp.tistory.com/33

https://developer.apple.com/documentation/metal/tailor_your_apps_for_apple_gpus_and_tile-based_deferred_rendering

https://developer.arm.com/documentation/102662/latest/

+ Recent posts