Direct3D 12 は Metal や Vulkan と同じ低レベル API に属します。新しい API Set ですが、GPU の世代更新に合わせたというよりも Direct3D 11 世代の再定義に近いものでした。ShaderModel は 5.1 のままで、GPU の新機能対応も当初は 11/12 両方に行われています。
D3D API | ShaderModel | OS | Windows 10 |
---|---|---|---|
Direct3D 11.0 | 5.0 | Vista/7 | |
Direct3D 11.1 | 5.0 | 7/8 | |
Direct3D 11.2 | 5.0 | 7/8.1 | |
Direct3D 11.3 | 5.1 | 10 | |
Direct3D 11.4 | 5.1 | 10 (1511) | November Update |
Direct3D 12 | 5.1 | 10 | |
Direct3D 12 | 6.0 | 10 (1607) | Anniversary Update |
Direct3D 12 | 6.1 | 10 (1709) | Fall Creators Update |
Direct3D 12 | 6.2 | 10 (1803) | April 2018 Update |
Direct3D 11 の Release は Windows 7 と同時ですが、Windows 10 になっても 11.3/11.4 と更新が続いていたことがわかります。しかしながら Windows 10 1607 以降は ShaderModel 6.0 も導入されており、機能面での違いも増えてきたように思います。
コメントで Vega の ShaderModel 6.0 の対応状況について情報を教えていただいたので、あらためてそれぞれの GPU で確認してみました。新しい世代の GPU はいずれも最新ドライバで 6.1 に対応していることがわかりました。
GPU | FL | SM | Driver | |
---|---|---|---|---|
GeForce GTX 1070 | Pascal | 12_1 | 6.1 | 397.44 |
GeForce GTX 960 | Maxwell 2 | 12_1 | 6.1 | 398.11 |
GeForce GTX 750 Ti | Maxwell 1 | 11_0 | 6.1 | 398.11 |
RADEON Vega 56 | GCN 5 Vega | 12_1 | 6.1 | 18.5.1 |
RADEON RX 480 | GCN 4 Polaris | 12_0 | 6.1 | 18.5.1 |
RADEON R7 (A10-7870K) | GCN 2 (1.1) | 12_0 | 6.1 | 18.5.1 |
Intel HD Graphcis 530 (i7-6700K) | Gen 9 | 12_1 | 6.1 | 23.20.16.4973 |
下記は ShaderModel 6.0 の wave 命令の lane 数
GPU | min | max | total | |
---|---|---|---|---|
GeForce GTX 1070 | Pascal | 32 | 32 | 30720 |
GeForce GTX 960 | Maxwell 2 | 32 | 32 | 16384 |
GeForce GTX 750 Ti | Maxwell 1 | 32 | 32 | 10240 |
RADEON Vega 56 | GCN 5 Vega | 64 | 64 | 3584 |
RADEON RX 480 | GCN 4 Polaris | 64 | 64 | 2304 |
RADEON R7 (A10-7870K) | GCN 2 (1.1) | 64 | 64 | 512 |
Intel HD Graphcis 530 (i7-6700K) | Gen 9 | 8 | 32 | 768 |
その他 GPU 毎の対応状況の詳細は下記のページに載せています。
・Direct3D 12 (DirectX 12) Windows 詳細
GPU は頂点や Pixel のように大量のデータを扱います。これは並列化が容易なので、CPU の Multi Thread と同じように複数の Shader Core で分散実行しています。
CPU と異なっているのは、一定の Thread Group (wave) 毎に実行する命令 (Instruction) を共有していることです。同じ Instruction で同じ演算を行うという意味では SIMD に近いのですが、各 Thread はそれぞれ単一の Scalar 要素にだけアクセスできるようになっています。これは SIMT と呼ばれています。
例えば 4 並列の SIMT を考えてみると、Thread 0 は SIMD Vector Register の x だけ、Thread 1 は y だけ使って演算を行っていることになります。コード上は Scalar 演算と同等です。
ShaderModel 6.0 の wave 命令では、この Thread 毎の Scalar アクセスの制限が緩和されており、ある程度相互に情報をやり取りできるようになりました。先程の例でいえば、本来 Thread 0 しかアクセスできない x の要素を Thread 1~3 からも参照できることになります。
なお ShaderModel 6.0 からは ShaderCompiler が変更されているようです。5.1 までは fxc.exe (D3DCompiler_47.dll) ですが、6.0 以降は dxc.exe (dxcompiler.dll) を使います。
dxc shader.hlsl -T ps_6_0 -E pmain -Fo shader_ps.bin
dxc でコンパイルした bytecode はそのまま PipelineState (D3D12_GRAPHICS_PIPELINE_STATE_DESC) に渡すことができます。
PS_OUT pmain( VS_OUT pin )
{
PS_OUT pout;
float2 pos= pin.Position.xy;
if( WaveIsFirstLane() ){
pos.x*= 1.0f/500.0f;
pos.y*= 1.0f/500.0f;
}
pos= WaveReadLaneFirst( pos );
if( WaveIsFirstLane() ){
pout.Color= float4( 0.0f, 0.0f, 1.0f, 1.0f );
}else{
pout.Color= float4( pos.x, pos.y, 0.0f, 1.0f );
}
return pout;
}
少々わかりにくいですが、上の PixelShader で Wave Size (Lane 数) の違いを視覚化してみたものです。同一 Wave を同じカラーで塗りつぶします。
↑左から RADEON Vega56, Skylake (Intel HD Graphics 530), GeForce GTX1070
Vega が最も Lane 数が多いので tile の色分けがわかりやすくなっています。真ん中の Intel HD Graphcis が最も細かいことがわかります。
関連エントリ
・AMD Vega と Direct3D 12
・Direct3D 12 GPU GeForce GTX 1070 Pascal と RADEON RX 480 Polaris