DirectX12 / DirectX11.3 の新機能である ROV (Rasterizer Order View) を試してみました。対応している GPU は下記の通り。新 Maxwell (GTX900) と HD Graphics 系です。ただし Intel HD Graphics は Typed UAV がありません。両方対応しているのは Maxwell 2 だけとなっています。
ROV TypedUAV ---------------------------------------------------- GeForce Maxwell 2 Y Y (GTX900) GeForce Maxwell 1 N Y (GTX750) GeForce Kepler N N RADEON GCN 1.1 N Y RADEON GCN 1.0 N Y Intel HD Graphics Gen8 Y N (Broadwell) Intel HD Graphics Gen7.5 Y N (Haswell)
より詳しい機能表は下記のページに掲載しています。
・Direct3D 12 (DirectX 12) Windows 詳細
Shader は複数の Unit で同時に走るので、実行順番は保証されずバッファアクセスは競合します。しかしながら Pixel の出力自体は順序が保証されており結果は一定です。Blend や Depth/Stencil Test では同一の Draw Call で重ね書きしても正しい値で評価されます。
シェーダー内で任意に読み書きできる UAV はこのルールが適用されませんでした。ROV を使うと UAV の読み書きの整合性が取れるようになります。
(1) が通常の RenderTarget への描画です。
(2) は RenderTarget への書き込みと全く同じものを UAV にも書き込んでいます。擬似 MRT
// (1) hlsl
Texture2D ColorMap0 : register( t0 );
SamplerState Sampler0 : register( s0 );
struct PS_OUT {
float4 Color : SV_Target;
};
PS_OUT pmain( VS_OUT pin )
{
PS_OUT pout;
float4 color_map= ColorMap0.Sample( Sampler0, pin.Texcoord );
pout.Color= color_map;
return pout;
}
// (2) hlsl
Texture2D ColorMap0 : register( t0 );
SamplerState Sampler0 : register( s0 );
RWTexture2D UAVBuffer0 : register( u0 );
PS_OUT pmain( VS_OUT pin, float4 pos : SV_Position )
{
PS_OUT pout;
float4 color_map= ColorMap0.Sample( Sampler0, pin.Texcoord );
pout.Color= color_map;
UAVBuffer0[ uint2(pos.xy) ]= color_map;
return pout;
}
↑(2) の結果を 2つ並べて表示したのがこちらです。左が RTV (RenderTarget), 右が UAV です。使用したのは GeForce GTX960 (Maxwell2)。
↑右の UAV では重ね合わせに問題が生じています。並列度が上がるためプリミティブが小さい方が発生しやすいです。
これを ROV に変更したのが (3) です。
// (3) hlsl
Texture2D ColorMap0 : register( t0 );
SamplerState Sampler0 : register( s0 );
RasterizerOrderedTexture2D UAVBuffer0 : register( u0 );
PS_OUT pmain( VS_OUT pin, float4 pos : SV_Position )
{
PS_OUT pout;
float4 color_map= ColorMap0.Sample( Sampler0, pin.Texcoord );
pout.Color= color_map;
UAVBuffer0[ uint2(pos.xy) ]= color_map;
return pout;
}
↑ (3) の結果です。左が RTV (RenderTarget), 右が ROV。
ROV では重ね合わせが正しく行われ RTV と見た目が同一になりました。
さらに ROV では、RenderTarget と違って値をシェーダーで読むことができます。
(4) は Shader 内で Blend したもの。
// (4) hlsl
Texture2D ColorMap0 : register( t0 );
SamplerState Sampler0 : register( s0 );
RasterizerOrderedTexture2D UAVBuffer0 : register( u0 );
PS_OUT pmain( VS_OUT pin, float4 pos : SV_Position )
{
PS_OUT pout;
float4 color_map= ColorMap0.Sample( Sampler0, pin.Texcoord );
pout.Color= color_map;
float4 prev_color= UAVBuffer0[uint2(pos.xy)];
UAVBuffer0[uint2(pos.xy)]= prev_color * 0.7f + map * 0.7f;
return pout;
}
↑(4) の結果。左が RTV, 右が ROV の半透明合成
ROV はかなり自由度が高く MRT の制限がほぼ無くなったような印象です。パフォーマンス等は調べてませんがかなり応用が効きそうです。
なお Intel HD Graphics では Typed UAV が使えないので、今まで使用してきた下記のコードが動きません。
RWTexture2DUAVBuffer0 : register( u0 ); RasterizerOrderedTexture2D UAVBuffer0 : register( u0 );
一応対応してみました。UAV を R32_UINT として読み込みます。
// cpp HD Graphics
#define USE_TYPED_UAV 0
D3D12_RESOURCE_DESC res_desc;
flatlib::memory::MemClear( res_desc );
res_desc.Dimension= D3D12_RESOURCE_DIMENSION_TEXTURE2D;
res_desc.Alignment= 0;
res_desc.Width= width;
res_desc.Height= height;
res_desc.DepthOrArraySize= 1;
res_desc.MipLevels= 1;
#if USE_TYPED_UAV
res_desc.Format= DXGI_FORMAT_R8G8B8A8_UNORM;
#else
res_desc.Format= DXGI_FORMAT_R8G8B8A8_TYPELESS;
#endif
res_desc.SampleDesc.Count= 1;
res_desc.Layout= D3D12_TEXTURE_LAYOUT_UNKNOWN;
res_desc.Flags= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS|D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
D3D12_HEAP_PROPERTIES heap;
flatlib::memory::MemClear( heap );
heap.Type= D3D12_HEAP_TYPE_DEFAULT;
heap.CPUPageProperty= D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
heap.MemoryPoolPreference= D3D12_MEMORY_POOL_UNKNOWN;
D3D12_CLEAR_VALUE clear_value;
flatlib::memory::MemClear( clear_value );
clear_value.Format= DXGI_FORMAT_R8G8B8A8_UNORM;
iD3DDevice->CreateCommittedResource(
&heap,
D3D12_HEAP_FLAG_NONE,
&res_desc,
D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
&clear_value, IID_PPV_ARGS(&iUAVTexture0) );
D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc;
flatlib::memory::MemClear( uav_desc );
#if USE_TYPED_UAV
uav_desc.Format= DXGI_FORMAT_R8G8B8A8_UNORM;
#else
uav_desc.Format= DXGI_FORMAT_R32_UINT;
#endif
uav_desc.ViewDimension= D3D12_UAV_DIMENSION_TEXTURE2D;
UINT uav_descriptor_size= iD3DDevice->GetDescriptorHandleIncrementSize( D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV );
D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle= iHeapUAV->GetCPUDescriptorHandleForHeapStart();
cpu_handle.ptr+= uav_descriptor_size * (UAV_DYNAMIC_COUNT);
iD3DDevice->CreateUnorderedAccessView( iUAVTexture0, nullptr, &uav_desc, cpu_handle );
シェーダーは下記の通り。
// hlsl (HD Graphics)
//RWTexture2D UAVBuffer0 : register( u0 );
RasterizerOrderedTexture2D UAVBuffer0 : register( u0 );
PS_OUT pmain( VS_OUT pin, float4 pos : SV_Position )
{
PS_OUT pout;
float4 color_map= TextureMap0.Sample( Sampler0, pin.Texcoord );
pout.Color= color_map;
uint fetch= UAVBuffer0[ uint2(pos.xy) ];
float4 prev_color;
prev_color.x= ((fetch>> 0) & 255) * (1.0f/255.0f);
prev_color.y= ((fetch>> 8) & 255) * (1.0f/255.0f);
prev_color.z= ((fetch>>16) & 255) * (1.0f/255.0f);
prev_color.w= ((fetch>>24) & 255) * (1.0f/255.0f);
float4 color= saturate( prev_color * 0.7f + color_map * 0.7f );
UAVBuffer0[ uint2(pos.xy) ]= ((uint)(color.x * 255.0f) << 0)
|((uint)(color.y * 255.0f) << 8)
|((uint)(color.z * 255.0f) <<16)
|((uint)(color.w * 255.0f) <<24);
return pout;
}
これで Intel HD Graphics でも同等の結果になりました。
RADEON は残念ながら ROV 非対応です。下記は RADEON GCN 1.1 (UAV) の結果で、こちらも GeForce 同様ブロック状のノイズが生じています。
関連エントリ
・3D 低レベル API の違い Direct3D 12/Metal
・Direct3D 12 GeForce GTX970 は FeatureLevel 12_1 対応、Resource Bind/Heap Tier は低い
・3D 低レベル API の現状 Direct3D 12/Metal
・Direct3D 12 (DirectX12) GPU と API の対応表
・DirectX 12 (Direct3D 12) と GPU の対応