2019年12月4日水曜日

Developer PowerShell for VS 2019をx64に切り替える方法

Visual Studio 2019から,Developer PowerShell for VS 2019,というものが付いてきます.

これで,PowerShellからcl.exeを使うためにゴニョゴニョしなくて済む,と思ったら,コンパイラが32bit環境になっていました.

次のコマンドで確認したときに,パスがHostx86のx86になっていました.

(gcm cl).Source

どうしても64bit環境が使いたいのですが,コマンドプロンプトについては,x86,x86からx64のクロスコンパイル,x64,x64からx86のクロスコンパイル,としっかり組み合わせが用意されているのに,PowerShellについては用意されていません.

そこで,Developer PowerShell for VS 2019のリンクのプロパティを見て,実際に何をやっているのか確認してみました.

%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -noe -c "&{Import-Module """C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\Microsoft.VisualStudio.DevShell.dll""";Enter-VsDevShell インスタンスID}"

インスタンスIDの部分は,たぶん環境によって変わります.

どうやら,Enter-VsDevShellというものを呼び出しているようです.取り合えずヘルプを見てみましょう.

Get-Help -Detailed Enter-VsDevShell

非常に簡単なことしか書いていませんが,-DevCmdArgumentsというものがあることに気付きます.これに -help を渡してみます.

Enter-VsDevShell インスタンスID -DevCmdArguments -help

すると,どうやらvsdevcmd.batを裏で呼んでいるようです.これについても情報が少ないのですが,あれこれ調べた結果,-arch=x64を指定してやれば良さそうです.

Developer PowerShell for VS 2019のリンクのプロパティからリンク先を書き換えてみましょう.
%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -noe -c "&{Import-Module """C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\Microsoft.VisualStudio.DevShell.dll""";Enter-VsDevShell インスタンスID -DevCmdArguments -arch=x64}"

書き換えたら,そのリンクからPowerShellを起動します.そして,もう一度次のコマンドを実行します.

(gcm cl).Source

無事,Hostx64のx64が選択されています.

やってやりました!

via GIFMAGAZINE



ちなみにインスタンスIDは,書いてあるものを使えば良いのですが,間違えて消してしまったりした場合,vswhereというものを使えば見れるようです.

注意点として,どうもリンク先の長さに上限があるらしく,書き換え方次第では入りきらなくなるようです.

2019年12月3日火曜日

CMakeで生成したVisual C++のプロジェクトで例外を無効にする方法

CMake 3.15.5でVisual Studio 2019向けに生成したプロジェクトで,次のようなCMakeのコマンドを実行して確認する.

message("${CMAKE_CXX_FLAGS}")

すると,/EHscが入っていることが分かる.これを次のように削除する.

string(REPLACE " /EHsc" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")

すると,プロパティページの「C/C++」の「コード生成」の「C++の例外を有効にする」が空欄になる.合わせて,「_HAS_EXCEPTIONS=0」をtarget_compile_definitionsで定義してやれば,例外を無効にできる.

他に何か良い方法があれば教えて欲しい.

コマンドラインからVisual Studioの仕組みを学ぶ2

前回はこちら
コマンドラインからVisual Studioの仕組みを学ぶ

マクロによるDebug版とRelease版の切り替え


さて,次のように_DEBUGの有無でデバッグ時とそれ以外で処理を変える,といったコードを書いたことはあるでしょうか?

#include <iostream>
using namespace std;

int main()
{
#if defined(_DEBUG)
    cout << "Debug" << endl;
#else
    cout << "Release" << endl;
#endif
}

この例だとあまり意味はないですが,例えば開発中のみデバッグ機能を有効にしたり,特別にログを出力するようにするなど,こういった書き方をすることは良くあります.

ところで,この _DEBUG というマクロはどこで定義されているのでしょうか? また,定義されていたとすると,デバッグ機能を無効にしたい場合は,わざわざ消さないといけないのでしょうか?

そんな面倒なことはしません.コマンドラインのオプションでマクロを定義することができます.これにより,コンパイル時にデバッグ有り版とデバッグ無し版を切り替えることができます.

マクロの定義には "/D マクロ" という書き方をします.

次のコマンドでは,_DEBUGが定義された状態でコンパイルします.
cl /EHsc /D _DEBUG main.cpp

一方,次のコマンドでは,_DEBUGが定義されていない状態でコンパイルします.
cl /EHsc main.cpp

ソースコードを読んでいて,どこにも定義されていないマクロに遭遇したことがあるかもしれません.そういったマクロは,このようにコンパイル時に定義されているのです.

Visual Studioのプロパティとしては,C/C++のプリプロセッサのプリプロセッサの定義になります.このプロパティは ; 区切りでマクロが定義されていて,コンパイル時にはそれを分解して,それぞれを /D と一緒にコンパイラに渡しているわけです.

プロパティの左上にある構成をDebugやReleaseで切り替えると,このプリプロセッサの定義,が変わることが分かります.

Visual Studioでは,DebugやReleaseといった目的やx64などのプラットフォームの組み合わせ毎に,こういった定義を設定することができて,その設定に合わせてコンパイラにマクロを渡してくれているわけです.

clangの場合,-D でマクロが定義できます.似ていますね.そして,clangを使っているXcodeでも,同じようにマクロを定義する項目があります.

まとめ

/D マクロ,でコンパイル時にマクロを定義した状態でコンパイルすることができます.
DebugやReleaseといった構成やx64などのプラットフォームを切り替えると,プロパティのプリプロセッサの定義が切り替わり,/D の形にしてコンパイラに渡してくれます.

おまけ

Windows.hを開いてみると,色々とコードを変更するためのマクロが書いています.特に,NOMINMAXは,次のようなコードがちゃんとコンパイルできるようにするために必要です.

#include <iostream>
#include <cmath>

int main()
{
 std::cout << std::min(1, 2) << endl;
}

どうしてNOMINMAXが必要なのか,実際に確かめてみると理解が深まるかもしれません.

ついでに,_USE_MATH_DEFINESについても調べておくと,役立つときがあるかもしれません.

2019年12月2日月曜日

コマンドラインからVisual Studioの仕組みを学ぶ



Visual Studioそのものについて解説した本や,Visual C++について解説した本はいくつかあります.ただ,Visual Studioの場合,C#前提だったり,Visual C++についての本でもC++のコードを書くことが前提だったりして,自分でライブラリを作ったり,他のライブラリを利用したりする場合の方法についてはあまり詳しくなかったりします.

このツイートの後に,コマンドラインを覚えれば,色々なIDEの設定などが分かるのでは,という話になったものの,そういった解説って無いよね,という話になったので,ちょっと書いてみようかと.

前提として,Visual Studio 2019でC++の開発環境が入っている前提とします.
また,cdやmkdirなど基本的なコマンドラインの機能についてはこの解説には含めないので,必要に応じて調べてみてください.

コマンドラインを使えるようにする

スタートメニューからVisual Studio 2019のフォルダを探し,Developer Command Prompt for VS 2019かDeveloper PowerShell for VS 2019を起動します.

起動したら,次のコマンドを実行してみましょう.

cl /?

この,cl というのがC++のコンパイラで,/?を渡すことで指定できるオプションの一覧を表示したわけです.

Visual C++の設定の多くは,このclに渡すオプションを生成するために使われます.

そして,ここが肝心なポイントなのですが,色々なC++コンパイラでコマンドラインから指定できるオプションには,共通の機能を提供するものが多々あるのです.

つまり,コマンドラインのオプションが分かっていれば,このオプションを設定するIDEの設定項目はどれか,という探し方ができるので,普段Visual Studioを使っていて,突然Xcodeを使うことになったとしても,何となく設定する方法が分かってしまうのです.

コンパイルしてみる

よくあるHello, Worldをコンパイルしてみましょう.

// main.cpp
#include <iostream>
using namespace std;

int main()
{
    cout << "Hello, World!" << endl;
}
cl main.cpp

コンパイルすると,何やら警告が出ます.着目するポイントは,warning(警告)という単語の直後のC4530という警告の番号です.これをGoogleなどで検索すると,Microsoftのドキュメントが表示されます.

他にもエラーや警告が出た場合,必ずその直後にこういったエラーの種類を示す番号が出ているので,Microsoftのドキュメントで検索してみましょう.エラーや警告の理由が分かれば,修正もしやすくなります.

詳細については省くとして,取り合えず/EHscを指定しろ,ということなので,次のように指定してみましょう.

cl /EHsc main.cpp

これで,問題なくコンパイルできました.同じフォルダにmain.exeとmain.objというファイルができているはずです.

さて,このobjってなんでしょう? これは,C++のソースコードをコンパイルした結果です.これに,標準ライブラリなどの機能をリンクさせて,最終的に実行可能ファイル(exe)になるのです.

簡単なソースコードの場合,コンパイルも一瞬で終わるので気になりませんが,大規模になってくるとコンパイル時間は数十分,数時間,場合によっては一晩かかったりするかもしれません.

できれば,変更した部分だけコンパイルするようにしたいところですよね.IDEは,objとcppを比較して,更新されている場合だけコンパイルする,といったことをすることで,コンパイル時間を短くする手助けをしてくれています.

単純に比較しているわけではなく,ヘッダファイルを書き換えた場合,そのヘッダファイルをインクルードしているcppも変更の対象にしてくれたり,と色々と賢くやってくれています.

ちなみに,この/EHscというのは,プロジェクトのプロパティのC/C++のコード生成のC++の例外を有効にする,で設定できる値です.

インクルードパスを指定する


さて,続いてインクルードパスについて解説します.まずは,次のような構造で必要なファイルを用意してください.

main.cppがあって,subフォルダがあって,その中にheader.hとsub.cppがある状態です.

|- main.cpp
|- sub
|   |- header.h
|   |- sub.cpp

それぞれのファイルの中身は次のようになります.

// sub/header.h
#ifndef HEADER_H_INCLUDED
#define HEADER_H_INCLUDED

int f();

#endif // HEADER_H_INCLUDED

// sub/sub.cpp
#include "header.h"

int f()
{
    return -1;
}

// main.cpp
#include <iostream>
#include "sub/header.h"

using namespace std;

int main()
{
    cout << f() << endl;
}

これをコンパイルしてみましょう.

cl /EHsc main.cpp sub/sub.cpp

いくつか注目するポイントがあります.

1つ目は,#includeの後の書き方に2種類あることです.一般的に,<と>で囲まれている場合,コンパイラが定義したパスからの相対パスでインクルードするファイルを探し,"で囲まれている場合,コンパイルしているファイルのある場所からの相対パスでインクルードするファイルを探した後に,コンパイラが定義したパスからの相対パスでインクルードするファイルを探します.

2つ目は,sub/header.hという書き方です.1つ目の説明でも書いたように,インクルードするファイルの指定は相対パスになるので,フォルダを指定して,そのフォルダ以下にあるファイルを指定することも可能なのです.

さて,先ほどからコンパイラが定義した場所から,と言っていますが,それを追加する方法があります.まずは,main.cppを次のように書き換えてみましょう.

// main.cpp
#include <iostream>
#include 
using namespace std; int main() { cout << f() << endl; }

次のように,/I subを追加すると,コンパイルできます.

cl /EHsc /I sub main.cpp sub/sub.cpp

/I の後にパスを書くことで,コンパイラがインクルードするファイルを探すパスのリストに追加することができます.

一般的には,他のライブラリを利用する場合,そのライブラリのヘッダファイルなどは一か所にまとまっていて,そのルートフォルダへのパスを/Iで指定することで利用できるようにします.

これは,プロジェクトのプロパティでは,C/C++の全般の追加のインクルードディレクトリに当たります.この項目に ; 区切りで設定したパスは,区切りで分けられ,それぞれ/I に指定されます.

同様の設定は,Xcodeにも存在しますし,Xcodeが利用しているclangというコンパイラにも同様の設定があります.clangの場合 -I で指定します.そう,似ているんですよ.

まとめ

コマンドラインのコンパイラへの理解を深めることで,IDEの設定項目についての理解を深め,プロジェクトの環境を整えることができるようになります.

まだまだ説明したいことはありますが,長くなるのでまた分けて書こうと思います.

考えている話題は,こんな感じです.

  • 警告レベルの変更
  • 警告やエラーの抑制
  • 警告をエラーに
  • 最適化レベルの変更
  • リンク関係の設定
  • ソースコードからコンパイラに指示を出す方法
  • ビルドイベント
  • C++言語仕様の選択

2019年8月2日金曜日

XAudio2SamplesのDirectX::LoadWAVAudioFromFileExを読み解く

入門XAudio2を読んでいて,45ページ目から48行目でサラッと流されているWAVファイルの読み込み処理,DirectX::LoadWAVAudioFromFileExについて気になったので読み解いてみようかと.

Qiitaに書くのは何か違うかな,と思ったのでこっちで.

RIFFについては知っている前提で.

RIFF(Resource Interchange File Format)についてはこちらを参照(Microsoft Docs)

DirectX::LoadWAVAudioFromFileEx関数で読み取った結果は,次のようなDirectX::WAVData構造体に保存されます.

namespace DirectX
{
    struct WAVData
    {
        const WAVEFORMATEX* wfx;
        const uint8_t* startAudio;
        uint32_t audioBytes;
        uint32_t loopStart;
        uint32_t loopLength;
        const uint32_t* seek;
        uint32_t seekCount;
    };
}

で,そのDirectX::LoadWAVAudioFromFileEx関数の宣言は次のような感じです.

namespace DirectX
{
    HRESULT LoadWAVAudioFromFileEx(
        _In_z_ const wchar_t* szFileName,
        _Inout std::unique_ptr& wavData,
        _Out_ WavData& result
    );
}

この,_In_z_とか_Inout_は,ソースコード注釈言語(Source-code Annotation Language, SAL)です.詳しくは,Microsoft Docsを検索してください.簡単に言うと,Visual Studioがコードの使い方に問題がないかチェックするための付加情報です.

szFileNameはファイルパス,wavDataにはファイルのデータ全てが格納されて,WAVData::startAudioはその中の音データの開始位置を指します.

さて,中を見ていきましょう.最初の数行は,ファイルパスへのポインタがNULLだったらエラー,出力先を0初期化,というだけなので飛ばします.



DWORD bytesRead = 0;
HRESULT hr = LoadAudioFromFile(szFileName, wavData, &bytesRead);

早速新しい関数が出てきました.


static HRESULT LoadAudioFromFile(
    _In_z_ const wchar_t* szFileName,
    _Inout_ std::unique_ptr& wavData,
    _Out_ DWORD* bytesRead
);

単純に,szFileNameのパスにあるファイルを開いて,wavDataにファイルの内容を書き込み,bytesReadに読み取ったバイト数を書き込む,というだけの関数です.

もう少し詳しく見てみると,Windows 8以降の場合は,CreateFile2を使ってファイルを開き,それより前の場合はCreateFileWを使ってファイルを開くようになっています.ちょっと調べてみたんですが,敢えて使い分ける理由がサッとは見つかりませんでした.

そして,Vista以降ならGetFileInformationByHandleExを使い,それより前の場合はGetFileSizeExを使ってファイルサイズを取得しています.これも,わざわざ使い分ける理由がいまいち不明です.GetFileInformationByHandleExの場合,エラー処理が入っているので,何らかの理由でファイルサイズがちゃんと取得できないケースがあるのかもしれません.

そして,このファイルサイズが32bitで表現できる範囲を超える場合や,WAVファイルとして最低限必要なRIFFチャンクと"fmt "チャンクのサイズ以下の場合,エラーとしています.

ここまで来ると,あとはwavDataに必要なサイズ分のメモリを割り当て,ファイルから読み取るだけです.この関数内で勝手にメモリを割り当てるからunique_ptrにしているんでしょうね.

さて,DirectX::LoadWAVAudioFromFileExに戻ります.


bool dpds, seek;
hr = WaveFindFormatAndData(
    wavData.get(),
    bytesRead,
    &result.wfx,
    &result.startAudio,
    &result.audioBytes,
    dpds,
    seek
);

またまた新しい関数です.


static HRESULT WaveFindFormatAndData(
    _In_reads_bytes_(wavDataSize) const uint8_t* wavData,
    _In_ size_t wavDataSize,
    _Outptr_ const WAVEFORMATEX** pwfx,
    _Outptr_ const uint8_t** pdata,
    _Out_ uint32_t* dataSize,
    _Out_ bool& dpds,
    _Out_ bool& seek
);


  • wavData
    • WAVファイルのデータを渡す
  • wavDataSize
    • wavDataのサイズを渡す
  • pwfx
    • wavData中のfmtチャンクから,WAVEFORMATEXの位置へのポインタを返す
  • pdata
    • wavData中のdataチャンクから,音データの開始位置へのポインタを返す
  • dataSize
    • pdataのサイズを返す
  • dpds
    • dpdsチャンクの有無を返す.dpdsチャンクはxWMAフォーマットの場合に存在する.
  • seek
    • Xbox OneでサポートされているXMA2フォーマットの場合にtrueになる,とコメントを読み解くと書いてある.

さて,この中で最初に呼ばれているFindChunkという関数は,次のような関数です.


static const RIFFChunk* FindChunk(
    _In_reads_bytes_(sizeBytes) const uint8_t* data,
    _In_ size_t sizeBytes,
    _In_ uint32_t tag
)

dataの先頭がRIFFのヘッダ(4バイトのfourccと4バイトのチャンクサイズ)と仮定して,sizeBytes先までにtagと一致するfourccを持つチャンクを探してくれるだけの関数です.

この関数を使って,まずはRIFFチャンクを探します.このRIFFチャンクがなかったり,RIFFチャンクのRIFFヘッダの直後のfourccがWAVEまたはXWMAで無い場合は正しいファイルでない,とエラーを返しています.

RIFFチャンクからRIFFヘッダとfourcc分のバイト数を進めた位置からサブチャンクが始まります.次に,このサブチャンクからfmtチャンクを探します.fmtチャンクには,少なくともWAVEFORMAT構造体のデータが含まれています.

このWAVEFORMAT構造体にはwFormatTagという音データの形式が格納されていて,PCM(pulse code modulation)形式や,ADPCMフォーマットである,といったことを確認し,その上でfmtチャンクのサイズに問題がないか,dpdsやseekは設定できるか,といったことをチェックしています.

fmtチャンクが終わったら,チャンクの終端に進み,dataチャンクを探し,サイズが渡されたデータを超えていないかチェックをしたら,dataチャンクのデータ部分の先頭をpdataに設定して,dataチャンクのサイズをdataSizeに設定して,この関数は終了です.

DirectX::LoadWAVAudioFromFileExに戻ると,次はWaveFindLoopInfo関数です.



static HRESULT WaveFindLoopInfo(
    _In_reads_bytes_(wavDataSize) const uint8_t* wavData,
    _In_ size_t wavDataSize,
    _Out_ uint32_t* pLoopStart,
    _Out_ uint32_t* pLoopLength
);

wavData,wavDataSizeはWaveFindFormatAndDataと同じなのでスキップします.この関数は,WAVファイルからループの開始サンプル数とループの長さのサンプル数を,それぞれpLoopStartとpLoopLengthが指す先に格納してくれます.

この中では,RIFFチャンクがWAVEかどうか確認しています.xWMAはループ情報を含まないようです.そして,DLSチャンク(fourccはwsmp)またはSampleチャンク(fourccはsmpl)からループ情報を取り出しています.

DLSは,Downloadable Soundの略っぽいですが,該当するデータが手元に無いので詳細は不明です.

smplチャンクは,コード中ではMIDIチャンクと書いているので,MIDIで作成した音しかループ情報を含んでいないのでしょうか?

このループ情報の取り出しは,場合によってはスキップしても良いかもしれません.

DirectX::LoadWAVAudioFromFileExに戻ると,dpdsやseekがtrueかどうかで次のような関数を呼び出して,必要なデータを取り出しています.


static HRESULT WaveFindTable(
    _In_reads_bytes_(wavDataSize) const uint8_t* wavData,
    _In_ size_t wavDataSize,
    _In_ uint32_t tag,
    _Outptr_result_maybenull_ const uint32_t** pData,
    _Out_ uint32_t* dataCount
);


  • wavData,wavDataSize
    • おなじみWAVファイルの中身とサイズ
  • tag
    • RIFFチャンクのサブチャンクから抜き出すチャンクのfourcc
    • dpdsの場合,dpdsチャンク,seekの場合seekチャンクを指定
  • pData
    • tagで指定したチャンクのデータ部分へのポインタを返す
    • 4バイトのデータの配列になっている
  • dataCount
    • tagで指定したチャンク内にある4バイトのデータの数
これでようやく終わりです.

この関数は,ファイルデータをすべてメモリに載せた上での処理になっています.しかし実際には,効果音などの短い音であればメモリに載せられても,BGMなどのようにある程度のサイズがあるデータは,必要な部分を徐々に読み取って再生するのが一般的だと思うので,そういった場合は,FindChunkのように都度必要なチャンクを探して回るのではなく,チャンクを発見する度に必要な情報を埋めていって,といった形にする必要があるかもしれません.

2019年3月22日金曜日

d3d11.hからd3d11_4.hまでの変更点

d3d11_1.h

インターフェース

  • ID3D11BlendState1
    • GetDesc1を追加
  • ID3D11Device1
    • CreateBlendState1を追加
    • CreateDeferredContext1を追加
    • CreateDeviceContextStateを追加
    • CreateRasterizerState1を追加
    • GetImmediateContext1を追加
    • OpenSharedResource1を追加
    • OpenSharedResourceByNameを追加
  • ID3D11DeviceContext1
    • ClearViewを追加
    • CopySubresourceRegion1を追加
      • D3D11_COPY_FLAGS型のフラグを最後に追加
    • CSGetConstantBuffers1を追加
    • CSSetConstantBuffers1を追加
    • DiscardResourceを追加
    • DiscardViewを追加
    • DiscardView1を追加
    • DSGetConstantBuffers1を追加
    • DSSetConstantBuffers1を追加
    • GSGetConstantBuffers1を追加
    • GSSetConstantBuffers1を追加
    • HSGetConstantBuffers1を追加
    • HSSetConstantBuffers1を追加
    • PSGetConstantBuffers1を追加
    • PSSetConstantBuffers1を追加
    • SwapDeviceContextStateを追加
    • UpdateSubresource1を追加
      • D3D11_COPY_FLAGS型のフラグを最後に追加
    • VSGetConstantBuffers1を追加
    • VSSetConstantBuffers1を追加
  • ID3D11RasterizerState1
    • GetDesc1を追加
  • ID3D11VideoContext1
    • CheckCryptoSessionStatusを追加
    • DecoderEnableDownsamplingを追加
    • DecoderUpdateDownsamplingを追加
    • GetDataForNewHardwareKeyを追加
    • SubmitDecoderBuffers1を追加
    • VideoProcessorGetBehaviorHintsを追加
    • VideoProcessorGetOutputColorSpace1を追加
    • VideoProcessorGetOutputShaderUsageを追加
    • VideoProcessorGetStreamColorSpace1を追加
    • VideoProcessorGetStreamMirrorを追加
    • VideoProcessorSetOutputColorSpace1を追加
    • VideoProcessorSetOutputShaderUsageを追加
    • VideoProcessorSetStreamColorSpace1を追加
    • VideoProcessorSetStreamMirrorを追加
  • ID3D11VideoDevice1
    • CheckVideoDecoderDownsamplingを追加
    • GetCryptoSessionPrivateDataSizeを追加
    • GetVideoDecoderCapsを追加
    • RecommendVideoDecoderDownsampleParametersを追加
  • ID3D11VideoProcessorEnumerator1
    • CheckVideoProcessorFormatConversionを追加
  • ID3D11DeviceContextState
  • ID3DUserDefinedAnnotation

構造体

  • D3D11_BLEND_DESC1
    • RenderTargetがD3D11_RENDER_TARGET_BLEND_DESCからD3D11_RENDER_TARGET_BLEND_DESC1に変化
  • D3D11_KEY_EXCHANGE_HW_PROTECTION_DATA
  • D3D11_KEY_EXCHANGE_HW_PROTECTION_INPUT_DATA
  • D3D11_KEY_EXCHANGE_HW_PROTECTION_OUTPUT_DATA
  • D3D11_RASTERIZER_DESC1
    • ForcedSampleCountを追加
  • D3D11_RENDER_TARGET_BLEND_DESC1
    • LogicOpEnableを追加
    • LogicOpを追加
  • D3D11_VIDEO_DECODER_BEGIN_FRAME_CRYPTO_SESSION
  • D3D11_VIDEO_DECODER_BUFFER_DESC1
  • D3D11_VIDEO_DECODER_SUB_SAMPLE_MAPPING_BLOCK
  • D3D11_VIDEO_PROCESSOR_STREAM_BEHAVIOR_HINT
  • D3D11_VIDEO_SAMPLE_DESC

列挙型

  • D3D11_1_CREATE_DEVICE_CONTEXT_STATE_FLAG
  • D3D11_COPY_FLAGS
  • D3D11_CRYPTO_SESSION_STATUS
  • D3D11_LOGIC_OP
  • D3D11_VIDEO_DECODER_CAPS
  • D3D11_VIDEO_PROCESSOR_BEHAVIOR_HINTS

d3d11_2.h

インターフェース

  • ID3D11Device2
    • CheckMultisampleQualityLevels1を追加
      • D3D11_CHECK_MULTISAMPLE_QUALITY_LEVELS_FLAGを受け取るようになっている
    • CreateDeferredContext2を追加
    • GetImmediateContext2を追加
    • GetResourceTilingを追加
  • ID3D11DeviceContext2
    • BeginEventIntを追加
    • CopyTileMappingsを追加
    • CopyTilesを追加
    • EndEventを追加
    • IsAnnotationEnabledを追加
    • ResizeTilePoolを追加
    • SetMarkerIntを追加
    • TiledResourceBarrierを追加
    • UpdateTileMappingsを追加
    • UpdateTilesを追加

構造体

  • D3D11_PACKED_MIP_DESC
  • D3D11_SUBRESOURCE_TILING
  • D3D11_TILE_REGION_SIZE
  • D3D11_TILE_SHAPE
  • D3D11_TILED_RESOURCE_COORDINATE

  • D3D11_CHECK_MULTISAMPLE_QUALITY_LEVELS_FLAG
  • D3D11_TILE_COPY_FLAG
  • D3D11_TILE_MAPPING_FLAG
  • D3D11_TILE_RANGE_FLAG

d3d11_3.h

インターフェース

  • ID3D11Device3
    • CreateDeferredContext3を追加
    • CreateQuery1を追加
    • CreateRasterizerState2を追加
    • CreateRenderTargetView1を追加
    • CreateShaderResourceView1を追加
    • CreateTexture2D1を追加
    • CreateTexture3D1を追加
    • CreateunorderedAccessView1を追加
    • GetImmediateContext3を追加
    • ReadFromSubresourceを追加
    • WriteToSubresourceを追加
  • ID3D11DeviceContext3
    • Flush1を追加
      • D3D11_CONTEXT_TYPEおよびイベントハンドルを受け取るようになっている.
    • GetHardwareProtectionStateを追加
    • SetHardwareProtectionStateを追加
  • ID3D11DeviceContext4
    • Signalを追加
    • Waitを追加
  • ID3D11Fence
  • ID3D11Query1
    • GetDesc1を追加
  • ID3D11RasterizerState2
    • GetDesc2を追加
  • ID3D11RenderTargetView1
    • GetDesc1を追加
  • ID3D11ShaderResourceView1
    • GetDesc1を追加
  • ID3D11Texture2D1
    • GetDesc1を追加
  • ID3D11Texture3D1
    • GetDesc1を追加
  • ID3D11UnorderedAccessView1
    • GetDesc1を追加

構造体

  • D3D11_QUERY_DESC1
    • D3D11_CONTEXT_TYPE型のContextTypeを最後に追加
  • D3D11_RASTERIZER_DESC2
    • D3D11_CONSERVATIVE_RASTERIZATION_MODE型のConservativeRasterを最後に追加
  • D3D11_RENDER_TARGET_VIEW_DESC1
    • Texture2DがD3D11_TEX2D_RTV1型に変化
    • Texture2DArrayがD3D11_TEX2D_ARRAY_RTV1型に変化
  • D3D11_SHADER_RESOURCE_VIEW_DESC1
    • Texture2DがD3D11_TEX2D_SRV1型に変化
    • Texture2DArrayがD3D11_TEX2D_ARRAY_SRV1型に変化
  • D3D11_TEX2D_ARRAY_RTV1
    • PlaneSliceを最後に追加
  • D3D11_TEX2D_ARRAY_SRV1
    • PlaneSliceを最後に追加
  • D3D11_TEX2D_ARRAY_UAV1
    • PlaneSliceを最後に追加
  • D3D11_TEX2D_RTV1
    • PlaneSliceを最後に追加
  • D3D11_TEX2D_SRV1
    • PlaneSliceを最後に追加
  • D3D11_TEX2D_UAV1
    • PlaneSliceを最後に追加
  • D3D11_TEXTURE2D_DESC1
    • D3D11_TEXTURE_LAYOUT型のTextureLayoutを最後に追加
  • D3D11_TEXTURE3D_DESC1
    • D3D11_TEXTURE_LAYOUT型のTextureLayoutを最後に追加
  • D3D11_UNORDERED_ACCESS_VIEW_DESC1
    • Texture2DがD3D11_TEX2D_UAV1型に変化
    • Texture2DArrayがD3D11_TEX2D_ARRAY_UAV1型に変化

列挙型

  • D3D11_CONSERVATIVE_RASTERIZATION_MODE
  • D3D11_CONTEXT_TYPE
  • D3D11_FENCE_FLAG
  • D3D11_TEXTURE_LAYOUT

d3d11_4.h

インターフェース

  • ID3D11Device4
    • RegisterDeviceRemovedEventを追加
    • UnregisterDeviceremovedを追加
  • IDeD11Device5
    • CreateFenceを追加
    • OpenSharedFenceを追加
  • ID3D11Multithread
  • ID3D11VideoContext2
    • VideoProcessorGetOutputHDRMetaDataを追加
    • VideoProcessorGetStreamHDRMetaDataを追加
    • VideoProcessorSetOutputHDRMetaDataを追加
    • VideoProcessorSetStreamHDRMetaDataを追加

構造体

  • D3D11_FEATURE_DATA_D3D11_OPTIONS4

2019年2月22日金曜日

プログラムの設計をする際に考えていること

ふと思うところがあり,自分がプログラムを設計する際に考えていることを言葉にしてみようと思う.

まず,最初に考えるのは,最終的に用意したいデータは何なのか,ということである.

例えば,最終的に予算の合計が知りたい,だったり,最終的に画像ファイルが生成されていて欲しい,だったり,何らかのキーワードの有無が知りたい,であったりだ.

これがはっきりしていない場合,まずはこれをはっきりさせるために,情報収集を行う.例えば,発注者に話を聞いたり,ゲームの場合,仮組みをして動きや見た目を提示して,想定と合っているかの確認を行う.

次に,手元にあるデータから,最終的に必要なデータまでの加工手順を考える.ここで手順が思いつかない場合,最終的なデータを直接生成できるデータを考える.そのデータを生成できるデータを考えて,と逆算を行っていく.どうしても辿り着けない場合,何らかの情報が欠けていると思われるので,欠けている情報を明確にする.

例えば,画像を良い感じに加工して,と言われても,その良い感じがなんなのか分からないので,熱い感じなのか,寒い感じなのか,明るい感じなのか,暗い感じなのか,といったことを追加で入力してもらう必要があるかもしれない.
(そもそもそんな指示を出すのはどうかという話は置いておく)

また,今後追加で拡張などをして欲しい,といった要望があることも考えて,どういった拡張が考えられるだろう,といったことも考えておく.

ここまで来て,ようやくプログラムの設計に入るのだけれど,ここでいきなりクラスやアーキテクチャなどの設計に入るわけではない.

まずは,途中で必要となる要素の実験を行う.例えば,ファイルが作られた日が必要だとして,対象としている環境すべてで取得可能か確認を行う.もし不可能な環境があれば,他の方法を検討する必要がある.

他にも,想定している機能拡張がある場合にやりやすい方法は何だろう,と考える.なるべくシンプルな機能に分解しておいて,それらを組み合わせて実装するようにしておくと,後々組み合わせるだけで済み,追加部分だけのテストで良いこともある.

いくつかの実験を行い,一通りのパーツが揃ったら,ようやくクラスやアーキテクチャの設計に入る.

この段階で考えるのは,用意したプログラムを他のプログラマが利用するかどうか,ということ.

利用しない場合,ぶっちゃけ多少適当に作ったとしても,他に影響は無いので,サクッと作って素早く提供できる方が良い.

問題になるのは,他のプログラマが利用する場合だ.

下手にgetterやsetterを作ろうものなら,思わぬ使い方をされて,下手に修正できなくなる.

他のプログラマがどういう風に利用するかを考え,想定する使い方をする場合は使いやすく,想定しない使い方をしようとすると,途端に面倒になるような形が望ましい.

これは,ある程度利用するプログラミング言語に関する知識が必要となる.

例えば,C++であれば,あるクラスのオブジェクトはコピーできないようにしておく,getterは値のコピーを返し,オブジェクトが持っている値そのものは,簡単には変更できないようにする,などが考えられる.

C++の場合,アドレスが分かれば無理矢理書き換えもできるが,そこまでやる相手のことを考え始めると,実装速度や処理速度が犠牲になるのでやらない.

他にも,インターフェースである関数名や仮引数名を分かりやすいものにする,などで利用するプログラマが勘違いしたりして変な使い方をしないようにする.

例えば,音量を指定する機能を実装する場合に,それを0から1の浮動小数点で指定するのか,0から100のパーセンテージを表す整数で指定するのか,はたまた実際にスピーカーに渡せる最大値と0の範囲で指定するのか,と色々考えられるが,これがvolume,としか書いていなければ,ドキュメントを信じるか,実装を読み解くしか無い.

ちなみに,同一システム内に上記が混在しているのを見たことがある.

また,統一感,というのも重要になる.例えば,セーブロードの機能が,全体的にsaveとloadという名前で実装されているところに,storeとrestoreを導入する,というのは統一感が取れていない.

統一感を出すには,実装しているシステム中で一般的に使われている単語の用法から外れないように単語を選択していく必要がある.

この辺は,驚きを最小にする,ということを意識するようにしている.

また,実装しているシステムだけでなく,利用しているAPIやプログラミング言語の公式ドキュメント,世に溢れるオープンソースから類似の実装を読み,よく使われている名前を検討する.

よく使われている名前を使っておくと,検索する際にも似たような情報を拾いやすくなる.また,意味の取り違えも減る.

ここまでやって,ようやく多少は間違った使い方をされにくい設計ができる.

後は,考えたことをコードに落とし込んでいくだけである.

書いてみたけど,若干とりとめが無い,ポエムになってしまった感があるなぁ・・・・・・