14
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

グラフィックス全般Advent Calendar 2023

Day 13

GPU の機械語コードの逆アセンブリを読んでみる - RDNA2

Last updated at Posted at 2023-12-12

はじめに

グラフィックス全般 Advent Calendar 2023 13日目の記事になります.

きっかけ

GLSL で mix を使って2つの値から1つを選択する際に, abvec3 を直接用いずに vec3 に変換して渡しているコードを見かけた.
bvec3 で渡す場合と vec3 で渡す場合の, 最終的にGPUで実行されるコードの違いが気になったので, 簡単に調べてみることにした.

関連する GLSL の mix のオーバーロード:

// vecN を取るもの
genType mix(genType x, genType y, genType a);
// bvecN を取るもの
genType mix(genType x, genType y, genBType a);

題材となる GLSL によるフラグメントシェーダ

egui_glow の実装に含まれる, リニアから sRGB への変換をするためのコード (MIT, Apache).
https://github.com/emilk/egui/blob/8d4de866d4da1835b31e85f9068a5257e6ccbccb/crates/egui_glow/src/shader/fragment.glsl#L20-L25

mix の第3引数 abvec3 cutoff をそのまま渡さず, vec3 に変換して渡している.
GPUが最終的に実行するコードでは, どのような違いが出るだろうか?

#version 450

layout(location = 0) out vec4 out_color;

layout(location = 0) in vec3 in_color;

// リニア から ガンマ(sRGB) へ変換する
vec3 srgb_gamma_from_linear(vec3 rgb) {
    bvec3 cutoff = lessThan(rgb, vec3(0.0031308));
    vec3 lower = rgb * vec3(12.92);
    vec3 higher = vec3(1.055) * pow(rgb, vec3(1.0 / 2.4)) - vec3(0.055);
    // 👇 コード生成の違いは?
-   return mix(higher, lower, vec3(cutoff));
+   return mix(higher, lower, cutoff);
}

void main()
{
    vec3 converted = srgb_gamma_from_linear(in_color);
	out_color = vec4(converted, 1.0);
}

ISA コード とは

ISA とは Instruction Set Architecture (命令セットアーキテクチャ) の頭字語だが,
GPU の文脈では,
テキストのシェーディング言語や IR (SPIR-V や DXIL/DXBC) からドライバによって変換され,
最終的に実行される GPU 固有のコードがよくその GPU の 「ISA コード」 と呼ばれており,
さらに 「ISA コード」 が単純に 「ISA」 と呼ばれることもよくある.

作られるタイミング

最終的な ISA コードが生成されるタイミングは例えば, コンピュートやレイトレーシングパイプラインを除けば,
vkCreateGraphicsPipelines(Vulkan), ID3D12Device::CreateGraphicsPipelineState(DX12), MTLDevice::newRenderPipelineStateWithDescriptor(Metal)
になる.

つまり, 基本的にはシェーダコードのほかに,
パイプラインステートやレンダーターゲットの構成などが全て定まり描画に必要な情報が全て揃った段階になる1.

なぜ RDNA2 か

AMD の RDNA22 や RDNA33 などのアーキテクチャではその ISA の仕様書が公開されており,
ドライバとは分離したコンパイラも利用できるため今回のような目的には都合がいい.
(他には Intel も情報公開が多い模様 - 「ドライバを一から書けるくらい」とか45)

RDNA2 の ISA コード の逆アセンブリ

RGA (Radeon™ GPU Analyzer) が独立して公開されているが,
ここでは Shader Playground の機能にインテグレーションされたものを用いる.

コンパイル条件:

Radeon GPU Analyzer 2.7.1, gfx1035, OpenGL

vec3 バージョンの RDNA2 の ISA コード:

sgpr_count(6)
vgpr_count(8)
wave_size(64)

s_version     UC_VERSION_GFX10 | UC_VERSION_W64_BIT   // 000000000000: B0802004
s_inst_prefetch  0x0003                               // 000000000004: BFA00003
s_mov_b32     m0, s6                                  // 000000000008: BEFC0306
v_interp_p1_f32  v2, v0, attr0.x                      // 00000000000C: C8080000
v_interp_p1_f32  v3, v0, attr0.y                      // 000000000010: C80C0100
v_interp_p1_f32  v0, v0, attr0.z                      // 000000000014: C8000200
v_interp_p2_f32  v2, v1, attr0.x                      // 000000000018: C8090001
v_interp_p2_f32  v3, v1, attr0.y                      // 00000000001C: C80D0101
v_interp_p2_f32  v0, v1, attr0.z                      // 000000000020: C8010201
v_log_f32     v1, v2                                  // 000000000024: 7E024F02
v_log_f32     v4, v3                                  // 000000000028: 7E084F03
v_log_f32     v5, v0                                  // 00000000002C: 7E0A4F00
v_mul_legacy_f32  v1, lit(0x3ed55555), v1             // 000000000030: 0E0202FF 3ED55555
s_mov_b32     s0, lit(0x3f870a3d)                     // 000000000038: BE8003FF 3F870A3D
v_cmp_gt_f32  vcc, lit(0x3b4d2e1c), v2                // 000000000040: 7C0804FF 3B4D2E1C
v_mul_legacy_f32  v4, lit(0x3ed55555), v4             // 000000000048: 0E0808FF 3ED55555
v_exp_f32     v1, v1                                  // 000000000050: 7E024B01
v_mul_legacy_f32  v5, lit(0x3ed55555), v5             // 000000000054: 0E0A0AFF 3ED55555
v_exp_f32     v4, v4                                  // 00000000005C: 7E084B04
v_exp_f32     v5, v5                                  // 000000000060: 7E0A4B05
v_fmaak_f32   v1, s0, v1, lit(0xbd6147ae)             // 000000000064: 5A020200 BD6147AE
v_cndmask_b32  v6, 0, 1.0, vcc                        // 00000000006C: D5010006 01A9E480
v_fmaak_f32   v4, s0, v4, lit(0xbd6147ae)             // 000000000074: 5A080800 BD6147AE
v_fma_f32     v7, v2, lit(0x414eb852), -v1            // 00000000007C: D54B0007 8405FF02 414EB852
v_cmp_gt_f32  vcc, lit(0x3b4d2e1c), v3                // 000000000088: 7C0806FF 3B4D2E1C
v_fmaak_f32   v5, s0, v5, lit(0xbd6147ae)             // 000000000090: 5A0A0A00 BD6147AE
v_cndmask_b32  v2, 0, 1.0, vcc                        // 000000000098: D5010002 01A9E480
v_cmp_gt_f32  vcc, lit(0x3b4d2e1c), v0                // 0000000000A0: 7C0800FF 3B4D2E1C
v_fmac_legacy_f32  v1, v7, v6                         // 0000000000A8: 0C020D07
v_fma_f32     v3, v3, lit(0x414eb852), -v4            // 0000000000AC: D54B0003 8411FF03 414EB852
v_cndmask_b32  v6, 0, 1.0, vcc                        // 0000000000B8: D5010006 01A9E480
v_fma_f32     v0, v0, lit(0x414eb852), -v5            // 0000000000C0: D54B0000 8415FF00 414EB852
v_fma_legacy_f32  v2, v3, v2, v4                      // 0000000000CC: D5400002 04120503
v_mov_b32     v3, 1.0                                 // 0000000000D4: 7E0602F2
v_fma_legacy_f32  v0, v0, v6, v5                      // 0000000000D8: D5400000 04160D00
exp           mrt0, v1, v2, v0, v3 done vm            // 0000000000E0: F800180F 03000201
s_endpgm                                              // 0000000000E8: BF810000
...

bvec3 バージョンの RDNA2 の ISA コード:

sgpr_count(6)
vgpr_count(12)
wave_size(64)

s_version     UC_VERSION_GFX10 | UC_VERSION_W64_BIT   // 000000000000: B0802004
s_inst_prefetch  0x0003                               // 000000000004: BFA00003
s_mov_b32     m0, s6                                  // 000000000008: BEFC0306
v_interp_p1_f32  v2, v0, attr0.x                      // 00000000000C: C8080000
v_interp_p1_f32  v3, v0, attr0.y                      // 000000000010: C80C0100
v_interp_p1_f32  v0, v0, attr0.z                      // 000000000014: C8000200
v_interp_p2_f32  v2, v1, attr0.x                      // 000000000018: C8090001
v_interp_p2_f32  v3, v1, attr0.y                      // 00000000001C: C80D0101
v_interp_p2_f32  v0, v1, attr0.z                      // 000000000020: C8010201
v_log_f32     v1, v2                                  // 000000000024: 7E024F02
v_log_f32     v4, v3                                  // 000000000028: 7E084F03
v_log_f32     v5, v0                                  // 00000000002C: 7E0A4F00
v_mul_legacy_f32  v1, lit(0x3ed55555), v1             // 000000000030: 0E0202FF 3ED55555
s_mov_b32     s0, lit(0x3f870a3d)                     // 000000000038: BE8003FF 3F870A3D
v_mul_f32     v8, lit(0x414eb852), v0                 // 000000000040: 101000FF 414EB852
v_mul_legacy_f32  v4, lit(0x3ed55555), v4             // 000000000048: 0E0808FF 3ED55555
v_exp_f32     v1, v1                                  // 000000000050: 7E024B01
v_mul_legacy_f32  v5, lit(0x3ed55555), v5             // 000000000054: 0E0A0AFF 3ED55555
v_exp_f32     v4, v4                                  // 00000000005C: 7E084B04
v_exp_f32     v5, v5                                  // 000000000060: 7E0A4B05
v_fmaak_f32   v1, s0, v1, lit(0xbd6147ae)             // 000000000064: 5A020200 BD6147AE
v_mul_f32     v6, lit(0x414eb852), v2                 // 00000000006C: 100C04FF 414EB852
v_fmaak_f32   v4, s0, v4, lit(0xbd6147ae)             // 000000000074: 5A080800 BD6147AE
v_mul_f32     v7, lit(0x414eb852), v3                 // 00000000007C: 100E06FF 414EB852
v_cmp_gt_f32  vcc, lit(0x3b4d2e1c), v0                // 000000000084: 7C0800FF 3B4D2E1C
v_fmaak_f32   v5, s0, v5, lit(0xbd6147ae)             // 00000000008C: 5A0A0A00 BD6147AE
v_cmp_lt_f32  s[0:1], v3, lit(0x3b4d2e1c)             // 000000000094: D4010000 0001FF03 3B4D2E1C
v_cndmask_b32  v0, v5, v8, vcc                        // 0000000000A0: 02001105
v_cmp_gt_f32  vcc, lit(0x3b4d2e1c), v2                // 0000000000A4: 7C0804FF 3B4D2E1C
v_cndmask_b32  v1, v1, v6, vcc                        // 0000000000AC: 02020D01
v_mov_b32     v2, 1.0                                 // 0000000000B0: 7E0402F2
v_cndmask_b32  v3, v4, v7, s[0:1]                     // 0000000000B4: D5010003 00020F04
exp           mrt0, v1, v3, v0, v2 done vm            // 0000000000BC: F800180F 02000301
s_endpgm                                              // 0000000000C4: BF810000
...

in_color.rgb の3コンポーネント分の処理があるため, 見た目ではある程度の量のコードに見える.

in_color.r からレンダーターゲット出力まで

in_color から out_color への出力のうち, r コンポーネントのみに絞って抜粋する.

vec3 バージョン:

0x00000C	v_interp_p1_f32	v2, v0, attr0.x
0x000018	v_interp_p2_f32	v2, v1, attr0.x

0x000024	v_log_f32	v1, v2  
0x000030	v_mul_legacy_f32	v1, lit(0x3ed55555), v1
0x000038	s_mov_b32	s0, lit(0x3f870a3d)
0x000040	v_cmp_gt_f32	vcc, lit(0x3b4d2e1c), v2
0x000050	v_exp_f32	v1, v1
0x000064	v_fmaak_f32	v1, s0, v1, lit(0xbd6147ae)
0x00006C	v_cndmask_b32	v6, 0, 1.0, vcc
0x00007C	v_fma_f32	v7, v2, lit(0x414eb852), -v1
0x0000A8	v_fmac_legacy_f32	v1, v7, v6

0x0000E0	exp	mrt0, v1, v2, v0, v3 done vm

bvec3 バージョン:

0x00000C	v_interp_p1_f32	v2, v0, attr0.x
0x000018	v_interp_p2_f32	v2, v1, attr0.x

0x000024	v_log_f32	v1, v2
0x000030	v_mul_legacy_f32	v1, lit(0x3ed55555), v1
0x000038	s_mov_b32	s0, lit(0x3f870a3d)
0x000050	v_exp_f32	v1, v1
0x000064	v_fmaak_f32	v1, s0, v1, lit(0xbd6147ae)
0x00006C	v_mul_f32	v6, lit(0x414eb852), v2
0x0000A4	v_cmp_gt_f32	vcc, lit(0x3b4d2e1c), v2 
0x0000AC	v_cndmask_b32	v1, v1, v6, vcc

0x0000BC	exp	mrt0, v1, v3, v0, v2 done vm

bvec3 バージョンのほうが1命令だけ少ないことがわかる. 全体では3命令だけ少ない.
(差はそれだけか...)

登場する実行単位

work item:
最小の実行単位.
頂点シェーダの頂点, フラグメントシェーダのフラグメント, コンピュートシェーダの1スレッド...

wavefront:
同時に実行される 32個 または 64個の work item.
同じシェーダの1つ1つの命令が全ての work item で同時に(lock step で)実行される.
NVIDIA でいう warp.

登場するレジスタ

SGPR(Scalar General Purpose Register):
各32ビットの汎用レジスタ. wavefront ごとに存在し, その全ての work item で共有される.
s0-s105.
(レジスタ割当はシェーダコードやGPUの性能による)

VGPR(Vector General Purpose Register):
各32ビットの汎用レジスタ. wavefront の work item ごとに個別に存在し, SGPR よりも全体の数は限られている.
v0-v255.
レジスタ割当はシェーダコードやGPUの性能により,
原則としては割当てる数が少ないほど, 同時に実行する数を増やすことができる(occupancy を上げられる).

VCC(Vector Condition Code):
64ビット. wavefront について work item ごとに, ベクタ比較命令の結果(0 or 1)を保持する.

スカラ命令とベクタ命令

s_ で始まる命令は SALU(Scalar ALU) や SMEM(Scalar Memory) を含むスカラ命令であり,
wavefront ごとに1つの値を扱う.
制御フロー関連の命令もスカラ命令で, これは wavefront の work item が lock-step で実行されることとも整合する.

v_ で始まる命令は VALU(Vector ALU) や VMEM(Vector Memory) を含むベクタ命令であり,
wavefront の work item ごとに個別の値を扱う.

vec3 バージョン 詳説

頂点シェーダの出力 in_color.r を補間し v2

頂点シェーダの出力結果をこのフラグメント用に補間し, v2 に格納する.
ラスタライゼーションで得られたトライアングル上の重心座標を用いて, 頂点シェーダの出力結果を補間する.

0x00000C	v_interp_p1_f32	v2, v0, attr0.x
0x000018	v_interp_p2_f32	v2, v1, attr0.x

これらの命令は実際には MAD(積和) と言える (FMA(融合積和) かもしれないがわからない):

v_interp_p1_f32 VDST, VSRC, <ATTR, ATTRCHAN>
パラメータ補間し VGPR に代入; 1パス目.
VDST = P10 * VSRC + P0
v_interp_p2_f32 VDST, VSRC, <ATTR, ATTRCHAN>
パラメータ補間し VGPR に代入; 2パス目.
VDST = P20 * VSRC + VDST

今回は VSRC としてそれぞれ v0, v1 が渡されているが, これらには現在の work item が処理するフラグメントの, トライアングル上の重心座標(I, J)が入っている.

P0, P10, P20 はプリミティブの3頂点のアトリビュートのうち, ATTR, ATTRCHAN で選択されたものを指す.
当然プリミティブごとの値であり, LDS から読まれる.

つまり2つの MAD(or FMA) を用いて, VDST = P0 + I*P10 + J*P20 を計算している.

ATTR, ATTRCHAN は頂点アトリビュートとそのコンポーネントを選択するものであり, 今回は attr0.x (= in_color.r) が選択されている.

v2 から higher.r を計算し v1

vec3 higher = vec3(1.055) * pow(rgb, vec3(1.0 / 2.4)) - vec3(0.055);
(の第1コンポーネント部分)に相当する計算を行う.

// v1 = 1.055 * pow(v2, 1.0 / 2.4) -0.055;

0x000024	v_log_f32	v1, v2     
0x000030	v_mul_legacy_f32	v1, lit(0x3ed55555), v1
0x000038	s_mov_b32	s0, lit(0x3f870a3d)
0x000050	v_exp_f32	v1, v1
0x000064	v_fmaak_f32	v1, s0, v1, lit(0xbd6147ae)
1. pow(v2, 1.0 / 2.4) を計算し v1

pow(v2, 1.0 / 2.4) を計算するが, pow に相当する命令はなく,
exp2(log2(v2) * (1.0 / 2.4)) として計算する; $a^x = 2^{x \cdot \log_2 a}$

0x000024	v_log_f32	v1, v2                          // v1 = log2(v2);
0x000030	v_mul_legacy_f32	v1, lit(0x3ed55555), v1 // v1 *= 1.0 / 2.4;
0x000050	v_exp_f32	v1, v1                          // v1 = exp2(v1);

// 以上で v1 = exp2(log2(v2) * (1.0 / 2.4))
//          = pow(v2, 1.0 / 2.0)

ここで lit(0x3ed55555) は浮動小数点数 0.4166671.0 / 2.4.

v_exp_f32 VDST, SRC0
2が底の指数関数, VGPR に代入.
VDST = exp2(SRC0)
v_log_f32 VDST, SRC0
2が底の対数関数, VGPR に代入.
VDST = log2(SRC0)
v_mul_legacy_f32 VDST, SRC0, VSRC1
乗算, VGPR に代入.
VDST = SRC0 * VSRC1
2. higher.r を完成させ v1
0x000038	s_mov_b32	s0, lit(0x3f870a3d)          // s0 = 1.055;
0x000064	v_fmaak_f32	v1, s0, v1, lit(0xbd6147ae)  // v1 = s0 * v1 + -0.055;

// 以上で v1 = s0 * v1 + -0.055;
//          = 1.055 * pow(v2, 1.0 / 2.4) -0.055;
//          = `higher.r`;

ここで lit(0x3f870a3d) は浮動小数点数 1.055, lit(0xbd6147ae)-0.055.

s_mov_b32 SDST, SSRC0
SGPR に値を代入.
SDST = SSRC0
v_fmaak_f32 VDST, SRC0, VSRC1, K
乗算し即値を加算(FMA), VGPR に代入.
VDST = SRC0 * VSRC1 + K

大小比較をし結果を VCC へ

bvec3 cutoff = lessThan(rgb, vec3(0.0031308));
(の第1コンポーネント部分)に相当する計算を行う.

0x000040	v_cmp_gt_f32	vcc, lit(0x3b4d2e1c), v2 // vcc = 0.0031308 > v2 ? 1 : 0

ここで lit(0x3b4d2e1c) は浮動小数点数 0.0031308.

v_cmp_gt_f32 (VCC,) SRC0, VSRC1
SRC0 > VSRC1 ? 1 : 0 を VCC の work item 位置のビットに代入.
VCC[work_item_index] = SRC0 > VSRC1 ? 1 : 0

higher.rlower.r を比較結果で線形補間し v1

mix(higher.r, lower.r, vcc ? 1.0 : 0.0) に相当する計算を行う.
つまり higher.r + (lower.r - higher.r) * (vcc ? 1.0 : 0.0) のことだが,
このうち lower.r - higher.r を一気に求めている.

// v7 = v2 * 12.92 + -v1
//    = v2 * 12.92 - `higher.r`
//    = `lower.r` - `higher.r`
0x00007C	v_fma_f32	v7, v2, lit(0x414eb852), -v1

// v6 = vcc ? 1.0 : 0.0; 
0x00006C	v_cndmask_b32	v6, 0, 1.0, vcc

// v1 = v1 + v7 * v6;
//    = `higher.r` + (`lower.r` - `higher.r`) * (vcc ? 1.0 : 0.0);
//    = mix(`higher.r`, `lower.r`, vcc ? 1.0 : 0.0);
0x0000A8	v_fmac_legacy_f32	v1, v7, v6

ここで lit(0x414eb852) は 浮動小数点数 12.92.

v_fma_f32 VDST, SRC0, SRC1, SRC2
FMA, VGPR に代入.
VDST = SRC0 * SRC1 + SRC2
v_cndmask_b32 VDST, SRC0, VSRC1 (VOP2 encoding)
VCC (の work item 位置のビット) の値による conditional move, VGPR に代入.
VDST = VCC[work_item_index] ? VSRC1 : SRC0
VOP3 encoding の場合は VCC の代わりに SGPR の値で判断可能.
v_fmac_legacy_f32 VDST, SRC0, VSRC1
FMA, VGPR に加算.
VDST += SRC0 * VSRC1

レンダーターゲットに出力

// out_color = vec4(v1, v2, v0, v3);
0x0000E0	exp	mrt0, v1, v2, v0, v3 done vm
exp TARGET, VSRC0, VSRC1, VSRC2, VSRC3, DONE, VM
頂点/フラグメントシェーダの結果の出力.

TARGET として mrt0 (location=0 である out_color)

bvec3 バージョン 詳説

頂点シェーダの出力 in_color.r を補間し v2

vec3 バージョンに同じ.

// v2 = `in_color.x` の補間結果
0x00000C	v_interp_p1_f32	v2, v0, attr0.x
0x000018	v_interp_p2_f32	v2, v1, attr0.x

v2 から higher.r を計算し v1

vec3 バージョンに同じ.

0x000024	v_log_f32	v1, v2                          // v1 = log2(v2)
0x000030	v_mul_legacy_f32	v1, lit(0x3ed55555), v1 // v1 *= 1.0 / 2.4;
0x000038	s_mov_b32	s0, lit(0x3f870a3d)             // s0 = 1.055;
0x000050	v_exp_f32	v1, v1                          // v1 = exp2(v1);
0x000064	v_fmaak_f32	v1, s0, v1, lit(0xbd6147ae)     // v1 = s0 * v1 + -0.055;

// 以上で v1 = s0 * v1 + -0.055;
//          = 1.055 * pow(v2, 1.0 / 2.4) - 0.055
//          = `higher.r`

v2 から lower.r を計算し v6

まずここが vec3 バージョンと異なり,
lower.r を単独で計算し VGPR に代入している.

// v6 = 12.92 * v2
//    = `lower.r`;
0x00006C	v_mul_f32	v6, lit(0x414eb852), v2
v_mul_f32 VDST, SRC0, VSRC1
乗算, VGPR に代入.
VDST = SRC0 * VSRC1

大小比較から higher.rlower.r を選択し v1

bvec3 cutoff = lessThan(rgb, vec3(0.0031308));
(の第1コンポーネント部分)の計算のあと, 直接 conditional move を用いて結果を選択している.

// vcc = 0.0031308 > v2 ? 1 : 0;
0x0000A4	v_cmp_gt_f32	vcc, lit(0x3b4d2e1c), v2 
// v1 = vcc ? v6 : v1;
//    = vcc ? `lower.r` : `higher.r`;
0x0000AC	v_cndmask_b32	v1, v1, v6, vcc

レンダーターゲットに出力

vec3 バージョンに同じ.

// out_color = vec4(v1, v3, v0, v2);
0x0000BC	exp	mrt0, v1, v3, v0, v2 done vm

観察

頂点シェーダの出力結果の補間 (v_interp_p1_f32, v_interp_p1_f32)

グラフィクスAPIにおいて頂点入力レイアウトは固定機能であるパイプラインステートに含まれるが,
少なくとも RDNA2 では頂点シェーダの出力結果の補間はフラグメントシェーダの先頭にパッチされるコードで行われることがわかる.
(それを行うためのハードウェアがあるわけではない)

varying (補間される頂点シェーダの出力結果) の数は VGPR の使用数に影響する, と言及されることがあるが,
v_interp_p1_f32v_interp_p1_f32 の使用を見るとそれがよくわかる.

ブレンド

ハードウェアによってはブレンド処理が専用のハードウェアでなくマイクロコードで行われる(フラグメントシェーダのISAコードの最後尾にパッチされる)と言われることがある1が,
どうやら AMD はそうではない.

これはモバイルで顕著, もしくは顕著だったようで, PowerVR では Rogue の次の A-Series アーキテクチャから専用のハードウェアに変更された6ほか,
そもそも Metal の MSL では GLES の拡張の shader framebuffer fetch のように, レンダーターゲットの値をフラグメントシェーダから扱える(programmable blending).

pow (任意の底の指数関数)

(他の GPU や CPU で使える SIMD 命令とかでもそうだと思われるが)
pow に相当する命令は RDNA2 に存在せず, 底が2の対数関数と指数関数の組み合わせで表現される7.

mixvec3 vs. bvec3

bvec3vec3 に直接的に変換しているような状況でも, 最終的な ISA コードの段階で単純な conditional move には変換されず FMA を使った線形補間になっていることがわかった.
conditional move で完結させるためには, GLSL の段階で bvec3 で mix を行う必要がある.

参考

Low-level GPU ISA assembly on RDNA (PDF)

  1. A little clarification on modern shader compile times – Yosoygames 2

  2. "RDNA 2" Instruction Set Architecture: Reference Guide (PDF)

  3. "RDNA3" Instruction Set Architecture: Reference Guide (PDF)

  4. You Compiled This, Driver. Trust Me…. – The Burning Basis Vector

  5. Hardware Specifications

  6. Fixed Function Changes & Scalability - Imagination Announces A-Series GPU Architecture: "Most Important Launch in 15 Years"

  7. Low-level thinking in high-level shading languages (Emil Persson, 2013)

14
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?