D3D12 ERROR: ID3D12CommandList::CopyTextureRegion: The region specified by D3D12_TEXTURE_COPY_LOCATION:PlacedFootprint extends past the end of the buffer it is placed on. The size required by PlacedFootprint is 8064, as the fields of PlacedFootprint::Placement are as follows: RowPitch is 256, Height is 32, and Format is R8G8B8A8_UNORM. PlacedFootprint::Offset is 0, which requires the buffer to have 8064 bytes; but the buffer only has 4096 bytes. [ RESOURCE_MANIPULATION ERROR #869: COPYTEXTUREREGION_INVALIDSRCPLACEMENT]
上記は32×32のテクスチャを読み込んでUploadのリソースから、DefaultのリソースにCopyTextureRegionをしようとしたところで怒られている。
今までDirectX12では読み込めないのかと思って、一度GDI+上で64×64に拡大してからテクスチャに使っていた。
今回の件とは関係ない調べ物をしていたときこんなサイトを見つけた。
そこからたどれるリンク先も含めて読むと衝撃的な内容とともに、いままでCreateCommittedResourceを使ってきたけど、実はCreatePlacedResourceの方が一般的で、CreateCommittedResourceは手抜きだと言うことがわかった。
CreateHeapを使って、リソースと紐づけるというのを自動的にCommittedはやってくれていたらしい。
4KB Alignment Resource
小さいサイズリソースをそのままCreateCommittedResourceに任せると64KB単位のリソースになってしまう。例えば4KB未満しかないリソースなのに64KBの領域が確保されてしまう。これはもったいない。
しかし、ある条件を満たしている場合4KB単位のリソースを作ることが出来るみたいだ。
マイクロソフトのサンプルではGetResourceAllocationInfoを使ってその条件を満たしているかを確認して処理を切り替えている。
ただし条件を満たしていない場合、D3D12 Debug Layerエラーが出力される。
D3D12 ERROR: ID3D12Device::GetResourceAllocationInfo: D3D12_RESOURCE_DESC::Alignment is invalid. The value is 4096. Resources with D3D12_RESOURCE_DESC::Flags with either D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET or D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL must set Alignment equal to 65536 (aka. D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT), or 0. [ STATE_CREATION ERROR #721: CREATERESOURCE_INVALIDALIGNMENT]
D3D12 ERROR: ID3D12Device::GetResourceAllocationInfo: D3D12_RESOURCE_DESC::Alignment cannot be 4KB, since D3D12_RESOURCE_DESC::Width, D3D12_RESOURCE_DESC::Height, and/ or D3D12_RESOURCE_DESC::DepthOrArraySize are too large. 4KB alignment requires the most detailed mip level be theoretically 64KB or smaller. A 4KB tile shape for D3D12_RESOURCE_DIMENSION_TEXTURE2D and R8G8B8A8_UNORM is 32 texels wide, 32 texels high, and 1 texels deep. When Width = 256, Height = 256, and Depth = 1, the number of tiles needed is 64, while 16 tiles is the maximum. [ STATE_CREATION ERROR #721: CREATERESOURCE_INVALIDALIGNMENT]
1つ目のエラーはレンダーターゲット、深度バッファの場合対象外。
2つ目のエラーはサイズの問題。
ピクセルと幅と高さから必要なサイズが一定以下の場合使えるらしい。
チェック関数として利用したいのにエラーが出てしまうのでこれは却下。
サンプルではチェックに通るデータのみで利用しているのでエラーが出てない。
仕方がないので自前でチェックするようにした。
チェックに通った場合、今までのCreateCommittedResourceではなく、CreateHeap & CreatePlacedResourceでリソースを用意する。
このときCreatePlacedResourceに渡すD3D12_RESOURCE_DESCのAlignmentにD3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENTをセットしてやると4KBアライメントのリソースが作れる。
最後に、今まで使っていなかった新しいリソースバリア(D3D12_RESOURCE_BARRIER_TYPE_ALIASING)を使ってアクティブ化した後、Clear、Discard、Copyのいずれかを行うと初期化したことになるらしい。
Heap領域を単純にAliasingで切り替えたとしてもメタデータの準備が整っておらず最悪GPUがクラッシュするので、利用目的にあったリソースの初期化が必要。
Clear
レンダーターゲットの場合ClearRenderTargetView関数、深度バッファの場合ClearDepthStencilView関数を呼ぶことになる。
Discard
DiscardResource関数を呼ぶとリソースのメタデータが再初期化される。
この後全体を描画することがわかっている場合、Clearよりも高速に動作するみたい。
Copy
CopyResource関数、CopyBufferRegion関数、CopyTextureRegion関数でリソース全体をコピーする。
テクスチャのコピー
冒頭のエラーについては、エラー自体を回避するだけならUpload用のリソースのサイズを8064バイト用意すればいいだけだった。
ただ、元データは4096しかないし、FootPrintのRowPitchは128のハズなのに256になっている。データ全体をそのままコピーしても上半分しか埋まってなかった。
FootPrintのRowPitchに合わせてデータをコピーする必要があるようで、元データとRowPitchが一致してない場合、1行ずつ書き込んでやることにした。
tNum nByte = DirectX::BitsPerPixel( nFormat ) / 8 ;
tNum nSrcRowPitch = oDesc.Width * nByte ;
tNum nDstRowPitch = oFP.Footprint.RowPitch ;
if( nDstRowPitch == nSrcRowPitch ) {
memcpy( pMap, oImg.Ptr(), oImg.Count()) ;
} else {
for( tNum y = 0 ; y < oDesc.Height ; y++ ) {
memcpy( pMap + nDstRowPitch * y, oImg.Ptr() + nSrcRowPitch * y, nSrcRowPitch ) ;
}
}
8064という数字も最初意味が分からなかったけど、最終行が128バイト分不要なのでその分少なくなっていると理解した。
この処理によって、今まで64以下の場合と2のべき乗で無いサイズのテクスチャを事前にGDI+で拡大してから処理していたのが不要になった。
0 件のコメント:
コメントを投稿