はかせのラボ

私の頭の中を書いていく雑記ブログです

DirectX DirectX12でのWICを使ったpngの描画

あいさつ

どうも、はかせです。
今日は昨日のオムライスの逆襲なのか
飯テロを食らいました。

こ、こんな飯テロ効かねぇ!(究極のやせ我慢)

さてMSMVP懇親の飯テロをこらえたところで
話はまるっと変わりDirectX12です。

いよいよ今週の土曜日にCEDECがあります。
今日明日でなるべくDirectX12の知識を入れて、
臨みたいと思います。

今回のネタは
「DirectX12でWICを使ったpngの描画」
です。

pngを描画したい

前回まではDirectX12の概要的なものを抑えていきました。
その中でポリゴンを描画するということはできるようになりました。

ポリゴンが描画出来たら次はテクスチャでしょう。

今回はpng画像の描画として
このブログや私のTwitterのアイコンになっている
ハロを描画してみます。
f:id:hakase0274:20190409154315j:plain
(本当いつ見ても最高にぐうかわ)

どうやってやる?

DirectX12からは11まであったファイルの読み込みとかの
関数が軒並み無くなっているそうです。

なので自作のローダーとか使って
画像を読み込みそれをリソースに割り当てる必要があります。

ただ私は今までデコーダーというものを作ったことがありません。
一応pngがチャンクで分かれてて云々は知っているのですが、
どうにもどう起こせばいいかよくわかりません。

なので「directx12 png 描画」みたいなキーワードでググってたんですが、
英語記事含めヒットはせず・・・
ヒットしたとしても未解決の質問とかばっかり・・・

もうにっちもさっちもいかなくなり
Twitterに呟いたらですね、
通りがかった神様がお告げを授けてくださいました。

「WIC使えばBMPとかと同様に読み込めますよ」


MA・ZI・DE!?
その話を聞くまで私の中で
WIC=DirectX11専用みたいなイメージがありました。
(includeにd3d11.hあったし、ググっても全然記事とか出てこないし)

ただ調べてみたところ12版もあるらしく、
似たサンプルもネットに落ちていました。

それによくよく考えたらWICって
Windows Imaging Componentの略ですもんね。
DirectX11で使えてDirectX12で使えない方が不自然でしたね。

これはもうやるっきゃねぇ。
ということでWICを使ってやります。

テクスチャ読み込み

基本的にソースコードは後述の参考のものと
ほぼ同じなのでここでは私が変えた部分だけを。

参考元ではDDSを使って読み込んでいたのをWICに直しただけですね。

// Note: ComPtr's are CPU objects but this resource needs to stay in scope until
// the command list that references it has finished executing on the GPU.
// We will flush the GPU at the end of this method to ensure the resource is not
// prematurely destroyed.
CoInitializeEx(NULL, COINIT_MULTITHREADED);
ComPtr<ID3D12Resource> textureUploadHeap;

// Load texture from WIC.
{
	std::unique_ptr<uint8_t[]> wicData;
	D3D12_SUBRESOURCE_DATA subresouceData;
	ThrowIfFailed(LoadWICTextureFromFile(m_device.Get(),
		L"Haro.png",
		&m_texture, 
		wicData, 
		subresouceData));
	D3D12_RESOURCE_DESC textureDesc = m_texture->GetDesc();

	const UINT64 uploadBufferSize  = GetRequiredIntermediateSize(m_texture.Get(), 0, 1);

	// Create the GPU upload buffer.
	ThrowIfFailed(m_device->CreateCommittedResource(
		&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
		D3D12_HEAP_FLAG_NONE,
		&CD3DX12_RESOURCE_DESC::Buffer(uploadBufferSize),
		D3D12_RESOURCE_STATE_GENERIC_READ,
		nullptr,
		IID_PPV_ARGS(&textureUploadHeap)));

	UpdateSubresources(m_commandList.Get(), 
		m_texture.Get(), 
		textureUploadHeap.Get(),
		0, 
		0, 
		1, 
		&subresouceData);
	m_commandList->ResourceBarrier(1, 
		&CD3DX12_RESOURCE_BARRIER::Transition(m_texture.Get(), 
		D3D12_RESOURCE_STATE_COPY_DEST,
			D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE));

	// Describe and create a SRV for the texture.
	D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
	srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
	srvDesc.Format = textureDesc.Format;
	srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
	srvDesc.Texture2D.MipLevels = 1;
	m_device->CreateShaderResourceView(m_texture.Get(), 
		&srvDesc, 
		m_srvHeap->GetCPUDescriptorHandleForHeapStart());

	m_commandList->DiscardResource(textureUploadHeap.Get(), nullptr);
}
CoUninitialize();

変更点としては
・subresouceDataを配列からただの変数に変更
・同時に配列数を扱っていた部分を1で固定
・COMなのでCoInitializeExとCoUninitialize追加

という感じになります。


これを実行すると
f:id:hakase0274:20191003221821p:plain
無事ハロが出ました!
ぐうかわ!!!

CoInitializeとCoInitializeEx

WIC使えますよーというありがたいお言葉を授けてくれたお方からですね、
「CoInitializeEx呼び出さないと使えませんよ」という忠告を受けました。
(ありがたや)

ただここで一つ疑問が生まれます。
私が以前DirectX11で使っていた時はCoInitializeを呼び出して
使っていました。

一体この二つは何が違うんだろうか?
ということでググってみました。

詳しく語るとこの記事の文字数が
大幅に膨れ上がってしまうため簡単にまとめると、

CoInitialize
シングルスレッドで使う
メッセージループに直接ないし間接的に関係している
スレッドでCOMを使う場合に指定

CoInitializeEx
マルチスレッドで使う
メッセージループに直接ないし間接的に関係していない
スレッドでCOMを使う場合に指定

といった感じ。
ちなみにCoInitializeExの第二引数に
どちらにするかを設定することができるため
基本はCoInitializeExを使うべきだそうです。

詳しくは下記の記事を参照
www.kekyo.net
www.kekyo.net

あとがき

今回はDirectX12でWICを使ってpngを描画してみました。
なぜかわからないですけど全然記事がネット上にないんですよね。

みんなググらなくてもこのぐらいできるからですかね?
ただ私はできなかったのでこの世のどこかにはこの記事の需要があると信じたい・・・

今日上がった私の友人の記事で
なぜかべらぼうに持ち上げられてますが、
全然そんな実力はないですw

ちなみにその記事がこちら
tkz9545.hatenablog.com

最近ブログを再開したようで
日々の進捗+おすすめのゲームの紹介をしています。

設計とかAIとかに興味があるらしいです。
多分そっち方面のまさかりだったら泣いて喜ぶと思うので是非。

それでは今回はこの辺でノシ

参考

www.shader.jp