DirectX12 で テクスチャ貼ってみた(1)

以前から随分と DirectX12(仮) 入門なネタが空いてしまいました。色々と追従やらドライバやらあったので、再開できるようにするまで時間がかかってしまいました。

エラーが出るようになってた

まず Build 10130 で気付いたのですが、以前のサンプル類を実行したときに、 “EXECUTION ERROR #552: COMMAND_ALLOCATOR_SYNC” のエラーが出るようになっていました。
コマンドキューの処理の問題があったようで、サンプル類を修正しました。

今回の結果

simple_texture_dx12
上記のように単色のテクスチャを描画できるようになりました。ただし画像からのテクスチャの場合ちょっと問題がありました。詳細は末尾にて。

テクスチャの準備

基本的には頂点バッファ同様に CreateCommittedResource で領域確保してそこに中身を書いていけばよさそうです。ただヒープの設定が異なるようで、以下のようにして作成します。

// テクスチャの準備.
D3D12_RESOURCE_DESC descResourceTex;
D3D12_HEAP_PROPERTIES heapProps;
ZeroMemory(&heapProps, sizeof(heapProps));
ZeroMemory(&descResourceTex, sizeof(descResourceTex));
heapProps.Type = D3D12_HEAP_TYPE_UPLOAD;
heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
heapProps.CreationNodeMask = 0;
heapProps.VisibleNodeMask = 0;
descResourceTex.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
descResourceTex.Width = 256;
descResourceTex.Height = 256;
descResourceTex.DepthOrArraySize = 1;
descResourceTex.MipLevels = 1;
descResourceTex.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
descResourceTex.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
descResourceTex.SampleDesc.Count = 1;
hr = dxDevice->CreateCommittedResource(
  &heapProps, 
  D3D12_HEAP_FLAG_NONE, 
  &descResourceTex, 
  D3D12_RESOURCE_STATE_GENERIC_READ, 
  nullptr, 
  IID_PPV_ARGS(texture.GetAddressOf()));

作成したテクスチャに対して以下のように単色でデータを書き込んでおきます。テクスチャの場合は Map/Unmap ではないようです。

D3D12_BOX box = { 0 };
box.right = 256;
box.bottom = 256;
box.back = 1;
uint32_t* p = (uint32_t*)malloc(256 * 256 * sizeof(uint32_t));
for (int i = 0;i < 256 * 256;++i) {
  p[i] = 0xFFFF0000; // 青に塗ってみる.
}
texture->WriteToSubresource(0, &box, p, 4 * 256, 4 * 256 * 256);

描画するための他のデータの準備

テクスチャの場合にはまだ用意するオブジェクトがあります。D3D11 ではおなじみのシェーダーリソースビューとサンプラーです。これらについては以下のようにして作成するようです。
 ただサンプラーは別のヒープとする必要があるようです。シェーダーリソースビューは定数バッファのビューと同じでよいみたいです。1つのヒープの中に複数のデータを格納するため、ヒープの先頭ポインタから適切なサイズでポインタをインクリメントしています。このあたりは実際のコードでは管理機構をちゃんと準備した方が良さそうです。

D3D12_SHADER_RESOURCE_VIEW_DESC descSRV;
ZeroMemory(&descSRV, sizeof(descSRV));
descSRV.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
descSRV.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
descSRV.Texture2D.MipLevels = 1;
descSRV.Texture2D.MostDetailedMip = 0;
descSRV.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; // これがないとおこられた.
handleSRV = descriptorHeapSRVCBV->GetCPUDescriptorHandleForHeapStart();
handleSRV.ptr += strideSize;
dxDevice->CreateShaderResourceView(texture.Get(), &descSRV, handleSRV);


handleSampler = descriptorHeapSMP->GetCPUDescriptorHandleForHeapStart();
D3D12_SAMPLER_DESC descSampler;
ZeroMemory(&descSampler, sizeof(descSampler));
descSampler.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
descSampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
descSampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
descSampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
descSampler.MaxLOD = FLT_MAX;
descSampler.MinLOD = -FLT_MAX;
descSampler.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER;
dxDevice->CreateSampler(&descSampler, handleSampler);

ちなみにフォーマット類はデータに合わせて適切に設定する必要がありますが、今回は例ということで決め打ちでやっています。

描画

基本的にはヒープを設定し、DescriptorTable をセットします。個別にシェーダーリソースビューをバインドするようなコードは不要のようです。

cmdList->SetGraphicsRootDescriptorTable(0, descriptorHeapSRVCBV->GetGPUDescriptorHandleForHeapStart());

問題点

まず、今回のような単色塗りテクスチャでも AMD のドライバでは機能しませんでした。ベータドライバということもあるでしょう。将来的にはどこかで対応されるとは思います。
 単色塗りテクスチャの時にはわからなかったのですが、これをファイルから読んだようなデータに拡張すると問題が発覚します。まず NVIDIA ドライバを使って描画したときには以下のようになります。
texture_nvidia_gpu
一方で、WARPデバイスを使って描画した場合以下のようになります。
texture_warp_gpu

世間では今回紹介したのと似たコードでテクスチャを生成しているため、NVIDIA 以外の他チップ、たとえばインテル内蔵の Intel HD Graphics シリーズなら WRAP デバイスと同様の挙動をするのかもしれません。
 描画結果からは、NVIDIA側はスウィズル化されているものとしてテクスチャを参照しているような気配です・・・画像からのテクスチャについてはもう少し追加で実験していこうと思います。

DirectX プログラミング
すらりんをフォローする
すらりん日記

コメント

タイトルとURLをコピーしました