はじめに
OSがWindows 8に変わりました。開発環境はVisual Studio 2012(VS2012))です。 以前のVisual Studio 2008からの引継ぎは、Visual Studioのデフォルトの変換で殆どの場合うまく行きますが、DirectX関連だけ問題がありました。 正確には、Visual Studioのversion違いの問題ではないのですが、D3DX11が使えなくなりました。 正確には、Windows 7時代まではDirectX関連のコンパイルにはDirectX SDKを別途インストールしてリンクしていまししたが、 Windows 8からはDirectX SDKがWindows SDKに取り込まれる形となり、別途インストールする必要がありません。 しかし、このタイミングでD3DX11がSDKに含まれなくなってしまいました。 対策自体はずいぶん前に調べたのですが、メモを残さずに忘れてしまっていました。 この度、オープンキャンパス用の古いコンテンツの再コンパイルが必要になり、同じ問題にハマって泣きそうになっています。 ので、対策を思い出しついでに、メモを残します。このメモ時点での環境。すでにVisual Studioはインストール済みの環境を想定しています。
- OS:Windows 8.1 64bit
- C++コンパイラ:Visual Studio 2012 pro
- SDK: Windows SDK(特にインストール作業などはいりません)
D3DX11を使った以前のコード
Win7時代に作った以前のコードでは、D3DX11を使っているわけですが、Win8に合わせてこれを全て排除しなければいけません。 では、D3DX11の何を使っていたのか。 それを洗い出すために、とりあえずソースから以下のinclude文をコメントアウトしてd3dx11.hを外します。#include <d3dx11.h> ↓ //#include <d3dx11.h> |
- D3DX11CompileFromFile
- D3DX11CreateShaderResourceViewFromFile
本ページでは、まず、D3DX11CompileFromFileをに対する回避先を記します。 D3DX11CreateShaderResourceViewFromFileに関してはまた次回に予定。
(2014/11/05追記)
コード整備の都合上、スクリーンショットを取る際に使う次のD3DX関数の代替手段から掲載しました。目次から探してください。
- D3DX11SaveTextureToFile
D3DX11CompileFromFileを使った以前の処理
まずは、D3DX11CompileFromFileの代替法を考えます。 D3DX11CompileFromFileを使った元のコードでは何をしていたのかというと、 例えば頂点シェーダに関して次の様な手続きを踏んでいました。- シェーダーのソースファイル(fx)をD3DX11CompileFromFile関数でコンパイル
- コンパイル後のバイナリデータをID3DBlobオブジェクトとして取得
- コンパイル後のバイナリデータをID3D11Device::CreateVertexShader関数に渡してシェーダオブジェクトを作成
/* シェーダ―のソースファイル(TCHAR* szFileName)をから 頂点シェーダオブジェクト(ID3D11VertexShader* pVertexShader)を作成することが目的 */ HRESULT CreateVertexShader( TCHAR* szFileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3D11Device* pd3dDevice, ID3D11VertexShader** resVS ){ DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS; #if defined( DEBUG ) || defined( _DEBUG ) dwShaderFlags |= D3DCOMPILE_DEBUG; #endif //ソースからコンパイル// ID3DBlob* pVSBlob; ID3DBlob* pErrorBlob; hr = D3DX11CompileFromFile( szFileName, NULL, NULL, szEntryPoint, szShaderModel, dwShaderFlags, 0, NULL, &pVSBlob, &pErrorBlob, NULL ); if( FAILED(hr) ) { if( pErrorBlob != NULL ) OutputDebugStringA( (char*)pErrorBlob->GetBufferPointer() ); if( pErrorBlob ) pErrorBlob->Release(); return hr; } if( pErrorBlob ) pErrorBlob->Release(); //この時点でコンパイル後のバイナリはpVSBlobに格納されている// // 頂点シェーダーオブジェクトの作成// ID3D11VertexShader* pVertexShader; hr = pd3dDevice->CreateVertexShader( pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), NULL, &pVertexShader ); //成功していればpVertexShaderが出来上がっている// *resVS = pVertexShader; pVSBlob->Release(); return hr; } |
Visual Studioによるシェーダの事前コンパイル
さて、では代替策はどうするかということですが、実行時コンパイルを辞めます。 VS2012ではシェーダをVisual Studio上で事前にコンパイル出来ます(VS2008は不明)。 で、どうやるのか、以下に手順を示します。まず、シェーダの拡張子を「hlslに変更」します。 そうしたら、そのソースをVisual Studioのプロジェクトに追加してください。 「メニュー」→「プロジェクト」→「既存の項目の追加」 でもいいですし、単純にソリューションエクスプローラーへドラッグしてもいいです。 そうしますと、あら不思議、HLSLコンパイラが使えるようになります。 どこが変わっているかというと、 「メニュー」→「プロジェクト」→「プロパティ」としてプロジェクトのプロパティを開きますと、 左のメニュー内に「HLSLコンパイラ」が追加されます(下図)。すばらしい!!
画像では全てのオプション画面を表示していますが、ここにエントリポイント名やシェーダ―モデルバージョンなんかを指定します。 また、右画面に「シェーダ―の種類」という項目がありますが、ここには頂点シェーダーやピクセルシェーダーなどの種類を、hlslファイルごとに設定してください。 ファイルごとに設定するには、このプロパティページを開いたまま、ソリューションエクスプローラー上でhlslファイルを選択すれば、 ファイルごとの設定ができます。プロパティページのタイトルバーに各hlslファイル名が表示されると思います。 この方法でファイルごとにエントリポイント名を変えることも可能ですが、全部mainで揃えてしまってもいいかと思います。 だって関数ごとにファイル分けるわけですし。
あとは「メニュー」→「ビルド」あたりからコンパイルしてやればシェーダソースがコンパイルされます。 コンパイル後に出来上がるバイナリのファイル名は"(ファイル名).cso"というものが推奨されているようですね。 ポイントとしては、以下に気を付けると楽です。
- シェーダ―関数ごとにファイルをシェーダ―ソースファイルを分ける。関数名.hlslなど。
- この結果、出来上がるバイナリはシェーダ―関数ごとに関数名.csoとなる
バイナリcsoファイルからシェーダオブジェクトの作成
シェーダーのバイナリができたので、今度はこれを基にシェーダ―オブジェクトを作ります。 ソースは以下の様になります。/* シェーダ―のバイナリcsoファイル(TCHAR* csoName)をから 頂点シェーダオブジェクト(ID3D11VertexShader* pVertexShader)を作成することが目的 */ HRESULT CreateVertexShader2( TCHAR* csoName, ID3D11Device* pd3dDevice, ID3D11VertexShader** resVS ){ //バイナリファイルを読み込む// FILE* fp = _tfopen(csoName, _T("rb")); if( fp == NULL){ return -1; } fseek(fp, 0, SEEK_END); long cso_sz = ftell(fp); fseek(fp, 0, SEEK_SET); unsigned char* cso_data = new unsigned char[cso_sz]; fread(cso_data, cso_sz, 1, fp); fclose(fp); //この時点でコンパイル後のバイナリはcso_dataに格納されている// // 頂点シェーダーオブジェクトの作成// ID3D11VertexShader* pVertexShader; HRESULT hr = m_pd3dDevice->CreateVertexShader( cso_data, cso_sz, NULL, &pVertexShader ); //成功していればpVertexShaderが出来上がっている// *resVS = pVertexShader; delete [] cso_data; return hr; } |
これで、D3DX11CompileFromFileに関しては使用を回避できました。
余談
今回の様に事前コンパイルが嫌だって方には、D3DCompileという関数で実行時コンパイルする方法もある様です。 しかしその場合にはD3DCompiler_**.dllが配布時に必要な様子。 D3DXの廃止はまあコード書くには面倒なんだけど、配布時には「d3dx**_**.dllが無い」というユーザーからのクレームを無くせるという点で素晴らしいので、 D3DCompileを使ってしまってはこのメリットすら無くなってしまい元も子もない気がします。 私の開発しているAIScopeでは、基本OpenGL描画で3D visionとかで遊びたいマニアだけDirectX使ってくださいというスタンスなので、 OpenGLしかつかなわい時でもdllが必要ってのはちょっと。まあ、Visual Studioで遅延読み込み設定すればいいのですが、 開発者って殆どのマシンにSDK入れちゃってたりしてユーザー環境の再現は何だかんだザルなんですよね(私だけですかね)。 だからどのdllが標準で入っていないものなのか、クレームが来るまで気付かないことが良くあります。D3DX11CreateShaderResourceViewFromFileの回避策はまた調べて次回以降に書きます。
目的だったオープンキャンパス用コンテンツは、結局古いWin7環境で直しちゃいました。 D3DXだけでなく、XAudio2までwin7~8間で互換が無いってことなので、こりゃあ一筋縄ではいかないですね。 映像・音楽関連の7用アプリの開発環境としてwin7+VS2008環境を維持しなきゃならないっぽい。しかもバーチャルマシンでなく実機で。非エコですなぁ。。。