| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | ||||
| 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 |
- VGPR
- 번역
- optimization
- atmospheric
- scalar
- vulkan
- texture
- scattering
- unrealengine
- GPU
- Nanite
- DX12
- shader
- rendering
- SIMD
- wave
- hzb
- forward
- GPU Driven Rendering
- UE5
- SGPR
- deferred
- ShadowMap
- RayTracing
- ue4
- DirectX12
- Study
- Wavefront
- Graphics
- Shadow
- Today
- Total
RenderLog
Wave Intrinsics 본문
개요
레퍼런스1, 2, 3을 통해 GPU 가 실행하는 각각의 Work Item 이 Wavefront 라는 그룹단위로 실행되는 것을 알 수 있었습니다. Shading Model 6(SM6) 이상 부터는 Wavefront 관련 Intrinsics 가 추가되었고 이 기능을 활용할 수 있습니다. 이것에 대해서 알아봅시다.
내용
GPU 작업은 여러 카테고리로 분류됩니다. 전체 작업을 나타내는 Domain. 그리고 그 보다 작은 단위의 WorkGroup 이 있습니다. WorkGroup 은 동일한 Compute Unit 에 실행되며 WorkGroup 단위로 내부 작업을 동기화 하기에 좋습니다. 마지막으로 WorkGroup 은 각각의 WorkItem 들이 모여서 구성됩니다.
Work Gorup 과 Work Item 사이에 단위가 하나 더 있는데 그것이 바로 Wavefront (AMD 인 경우 Wavefront, Nvidia 는 Warp) 입니다. Wavefront 는 Compute Unit 에서 LockStep 으로 동시에 실행되는 Work Item의 묶음 입니다. (AMD Wavefront는 64개 Warp는 32개)
Wavefront 내의 WorkItem 들이 LockStep 으로 실행되기 때문에 이 장점을 활용할 수 있을 것입니다. 예를들면 Wavefront 내에서 가장 작은 값을 구한다거나 또는 Wavefront 내에 있는 RGB 값의 평균값을 구하는 것이 가능할 것입니다. 기존에 존재하던 DDX, DDY 도 Wavefront 내의 인접한 픽셀을 정보를 교환하는 방식으로 구현합니다. 이 내용 또한 레퍼런스2 에서 확인 했었습니다. SM6 부터는 명시적으로 Wavefront 내의 정보들을 활용할 수 있는 기능이 추가되었습니다. 레퍼런스5 는 SM6 에서 사용가능 한 Wave Intrinsics 함수를 명세합니다. Wave Intrinsics 를 사용하면 DDX, DDY 와 같은 함수도 직접 구현 할 수 있습니다.
레퍼런스1, 2, 3이나 이전의 다른 글을 통해서 Wave Intrinsics 를 간접적으로 접해왔는데, 오늘은 해당 내용을 직접 구현해보는 시간을 가져봅시다. 그리고 레퍼런스7 은 마이크로 소프트에서 제공하는 샘플로, DirectX12 를 사용하여 Wave Intrinsics 의 샘플을 보여줍니다. 오늘은 이 예제를 사용하여 Wave Intrinsics 를 구현하려고 합니다.
구현은 Vulkan API 와 HLSL language 를 사용하여 구현하였습니다. 레퍼런스6 과 같이 GLSL 은 Subgroup 이라는 이름으로 Wave Intrinsics 를 다룰 수 있습니다만 여기서는 HLSL 로 작성하고 ShaderConductor 를 통해 Spirv 로 변환하는 형태로 구현하였습니다.
오늘 구현해볼 것은 Wavefront 내의 RGB 값의 평균값을 계산하여 Wavefront 단위로 같은 값을 사용하도록 할 것입니다. 이것을 사용하여 Wavefront 크기로 확대된 픽셀을 렌더링하는 예제를 만들 것입니다. 오늘 구현을 위해 필요한 함수는 총 3가지 입니다. 이 세가지를 먼저 알아봅시다.
WaveActiveBallot(true) : uint4(총 64비트) 를 반환하며, Active lane 는 1, Helper lane 는 0을 설정하여 리턴
countbits(uint x) : uint 를 넘겨주는 1로 설정된 비트의 개수를 리턴
WaveActiveSum(T) : 모든 Active lane 의 T 값을 합산하여 돌려줍니다. 그리고 모든 Active lane 은 이 합산한 값을 가짐
아래는 입력 이미지를 Wave operation 을 적용하여 출력 이미지에 기록하는 Compute shader 입니다.
// 이미지를 복사하는 Compute shader
[numthreads(16, 16, 1)]
void main(uint3 GlobalInvocationID : SV_DispatchThreadID)
{
if (GlobalInvocationID.x >= ComputeCommon.Width || GlobalInvocationID.y >= ComputeCommon.Height)
return;
// 입력 이미지에서 컬러를 가져옵니다.
float3 rgb = inputImage[uint2(GlobalInvocationID.xy)].rgb;
// Wave operation test
if (ComputeCommon.UseWaveIntrinsics <= 0)
{
// Wave operation 을 사용하지 않는 경우 바로 출력 이미지에 rgb 기록
resultImage[int2(GlobalInvocationID.xy)] = float4(rgb, 1.0);
}
else
{
// Wave operation 을 사용하여 Wavefront 내의 rgb 값의 평균을 사용하도록 함
// Active lane 의 비트를 얻고, 총 Active lane 을 수를 얻습니다.
uint4 activeLaneMask = WaveActiveBallot(true);
uint numActiveLanes = countbits(activeLaneMask.x) + countbits(activeLaneMask.y) + countbits(activeLaneMask.z) + countbits(activeLaneMask.w);
// Active lane 의 rgb 값의 평균 값을 얻습니다.
float4 avgColor = float4(WaveActiveSum(rgb) / float(numActiveLanes), 1.0);
// 최종 결과를 출력 이미지에 기록합니다.
resultImage[int2(GlobalInvocationID.xy)] = avgColor;
}
}


구현 코드
https://github.com/scahp/jEngine/tree/WaveIntrinsics
레퍼런스
1. Introduction to compute shaders
7. Direct3D 12 shader model 6 wave intrinsics sample
'Graphics > Graphics' 카테고리의 다른 글
| [UE4] A Scalable and Production Ready Sky and Atmosphere Rendering Technique 리뷰 - 코드분석 (2/2) (0) | 2023.03.09 |
|---|---|
| [UE4] A Scalable and Production Ready Sky and Atmosphere Rendering Technique 리뷰 (1/2) (0) | 2023.03.07 |
| Variable Shading Rate(VRS) (2) | 2022.09.27 |
| [UE4 PBR] Split sum appoximation 리뷰 (2) | 2022.07.07 |
| Dual-depth relief interior mapping (0) | 2022.06.11 |