読者です 読者をやめる 読者になる 読者になる

syghの新フラグメント置き場

プログラミングTipsやコード断片の保管場所です。お絵描きもときどき載せます。

XAudio2とMedia FoundationでMP3/WMA再生

C++ DirectX MFC Windowsストアアプリ プログラミングTips

経緯

以前、DirectXの学習を兼ねて開発していたゲームエンジンC++で様々なDirectX APIを直接叩いていました)では、効果音 (SE) の再生にXACT3を、BGMの再生にDirectShowを使っていました。本当はBGMにもXACT3を使ってもよかったのですが、MP3/WMAフォーマットの音楽ファイルを直接ストリーム再生したかったのでDirectShowを使っていました。ちなみにMIDIを扱っていた頃は、SE/BGMともに知る人ぞ知る太古のAPI、DirectMusicを使っていました*1

ですが、Windows 8で導入されたWindowsストアアプリ(のちのWindows 10におけるユニバーサルWindowsプラットフォームアプリ)ではXACT3もDirectShowも使えなくなりました。デスクトップアプリではともに一応使えますが、Windows SDK 8.0以降ではXACT3は廃止されてしまっています。

Microsoftが推奨している新しいオーディオ系の代替APIはいくつかあります。これらはデスクトップアプリでもWindowsストアアプリでもほぼ同様に使えます。一部はXboxとも共通のAPIとなっています。

  • Windows Core Audio (Windows Vista以降における新しいオーディオ基盤)
  • XAudio2 (DirectSoundの後継)
  • Media Foundation (DirectShowの後継)

Core AudioにはWASAPIと呼ばれる、高音質な排他モードをサポートするライブラリも含まれています。しかしゲームエンジンに使うのであれば、同時発音の処理は必須であり、たとえ音質を犠牲にしてもミキシング機能をとる必要があるため、結局XAudio2を使わざるをえないようです。
最初に候補として検討したのは「DirectX Tool Kit (DirectXTK, DXTK) for Audio」です。DXTK AudioはXAudio2を使ったXNAライクの簡易音声再生補助ライブラリなのですが、どうもこのライブラリはWAVフォーマット (RIFF) にしか対応していないようです。DXTK AudioはまたWaveBank (xwb) にも対応しているのですが、XACT3やXNAで使用可能だったXMA/xWMA圧縮フォーマットには対応していません。つまりXACT3/XNA時代の資産はほとんど使えないといっても過言ではないでしょう。尺が比較的短いSEの再生には使えますが、BGMの再生となるとやや微妙です。また、ポーリングベースではなくイベントベースで実装されていることにも注意が必要です。

Media Foundation

XAudio2は、実はWMA (xWMA) 圧縮フォーマットのオーディオデータをデコードする機能を標準で備えているようです。旧DirectX SDK June 2010に付属するxWMAEncode.exeなどを使えば、ゲーム用途におけるBGMファイルのサイズ問題は一応クリアできるでしょう。ただし、Windows 8.xに標準搭載されているXAudio2 v2.8では、xWMAはサポートされていません。Windows 10に標準搭載されているXAudio2 v2.9では、xWMAのサポートが復活しているようですが、xWMAを使い続けるのはどうも微妙な感じがします。

かつてのDirectShowのように、MP3/WMAフォーマットのメディアファイルを直接ストリーム再生できるようにするには、それらのフォーマットに対応したデコーダーが必要となります。そこで出番となるのが、Media Foundation。やはりDirectShowの後継だけあって、MFが出てくるのは必然ともいえます。

MSDN Magazineに、WindowsストアアプリでXAudio2とMedia Foundationを使ってストリーム再生をするサンプル(C++/CX)が掲載されていました。

ただしこのMSDNサンプルは、オーディオプレイヤーの3大必須機能(Play/Pause/Stop)を実装していないうえにメモリやリソースの寿命管理がgdgdで、とても流用できるような類のものではありません。
まずデスクトップアプリ向けに書き直し、その後で改めてストアアプリ向けに再移植してみました。

サンプルコードは下記2つのソリューション&プロジェクトを含んでいます。DXTK (2015-08-18版) も比較を兼ねてお試しで使っているので、それぞれの環境に合わせたビルドが必要になります。

  1. MFCベースのデスクトップアプリ。ビルドにはVisual Studio 2015 Pro/Community以上が必要。
  2. Windows 8.1用のストアアプリ。ビルドにはVisual Studio 2013 Pro/Community以上が必要。

https://sygh-jp.github.io/content_hosting/my_program_ss/MyMfcDXTKAudioTest1_ss_2015_09_23a.png
https://sygh-jp.github.io/content_hosting/my_program_ss/MyWin81DXTKAudioTest1_ss_2015_09_23a.png

Windows 8.1ストアアプリではなく、Windows 10向けのUWPアプリにしてもよかったんですが、まだまともなWindows 10開発環境を準備できていないので今回はあきらめました。フルスクリーンだと上下左右の余白が寂しい感じです。
デスクトップ版はWindows SDK 10を使っていますが、ターゲット環境をWindows 10に設定していないため、XAudio2 v2.9 (XAudio2_9.dll) ではなくv2.8 (XAudio2_8.dll) がリンクされます。Windows 7あるいはそれ以前のWindowsで動かすためには、旧DirectX SDK June 2010を使ってビルドし、XAudio2 v2.7 (XAudio2_7.dll) をリンクする必要があります。
※2016-03-17追記:
SharpDX 3.0.2を使ったWPFサンプルも追加しました。XAudio2 v2.7が使われるため、DirectX SDK June 2010もしくはエンドユーザーランタイムを導入すればWindows 7でも実行できます。今回はC++版のクラス設計をほぼそのまま移植しているため、一部C#らしくないコードになっていますが意図的なものです。

なお音声サンプルとして、「魔王魂」さんからダウンロードさせていただいたフリー素材を使用しています。音声ファイルの利用は下記サイトの利用規約に従ってください。

コードの二次利用は自由ですが、自己責任でお願いします。仮に損害が発生したとしても、当方は一切の責任を負いません。
なお、とるに足らないテスト用アプリではありますが、コレをこのままストアに提出することは絶対にやめてください。コレはあくまでサンプルコードです。そのまま提出してもたぶん審査で落ちるだけだと思いますが、もしコレをベースにして似たようなアプリを作成する場合、必ず何らかの独自機能をきちんと付加した上で、Package.appxmanifestのPublisherとPublisherDisplayNameをきちんと更新してください。この約束が守られていないアプリを発見した場合、しかるべきところに通報させていただきます。

C++/CX

簡単のため、ストアアプリ版のコードではMVVMは使っていません。MFC同様にコードビハインドを書いているだけなので、対比は楽だと思います。そのほか、ストアアプリ版はバックグラウンド再生にも対応させていないので、アプリが非アクティブになると再生が一時停止します。
しかし久しぶりにC++/CXを使ったんですが、PPL(特に例外処理)がめんどくさすぎて途中死にそうになりました。C++/CX版PPL (WinRT版PPL) は通常のC++版PPL (デスクトップ版PPL) と違って、C#のasync/awaitのようにタスク完了時に呼び出し元スレッドへのコンテキストの復帰を行なってくれるんですが、例外処理はどのみち煩雑すぎです。タスクチェーンの仕組みもよく考えられているとは思うんですが、やはり記述性や可読性の向上のためにresumable/awaitが欲しいところです。Visual C++ November 2013 CTPやVisual Studio 2015では実験的にresumable/awaitが実装されていますが、/awaitコンパイルオプション(/sdlや/RTCと共存できない)を明示的に指定する必要があったりして、まだ完成にはほど遠いようです。また、C#のasync/awaitとは違い、GUIアプリケーションのイベントハンドラーを気軽に(同期的に)書けるようになるという類のものではありません。やはりGUIフロントエンドはC#で記述するのが今のところベストだと思います。

ちなみにSharpDXにもXAudio2とMedia Foundationのローレベルなラッパーがあるのですが、リソース寿命管理や例外処理のことを考えると、世代別GCベースのC#から参照カウントベースのCOMコンポーネントを使うのはかなりしんどいです。DirectX全般にも該当しますが、もしC#でCOMコンポーネントを扱いたい場合、C++/CLIC++/CXでラップして、独自の高レベルI/Fを公開するライブラリを作成したほうがかえって効率的だと思います。

*1:単純に音楽ファイルを再生するだけであれば、WinMMやMCI (Media Control Interface) 関数、WMP (Windows Media Player) コンポーネント、あるいはWPF/WinRTのような高レベルAPIを利用するのが簡単なのですが、ゲーム開発で利用するとなるとパフォーマンス上の理由からDirectXファミリー一択になります。

Direct3D 12 (DirectX 12) の簡単なサンプル

DirectX GPU Direct3D C++ プログラミングTips ソフトウェアTips Windows Visual Studio MFC

本記事の内容は執筆時点の暫定仕様に基づくものです。実際の最終製品版とは異なる可能性があります

GDCの開催された2015年3月にはすでにDirect3D 12 (DirectX 12) の暫定ドキュメントとVisual Studio 2015 CTPが公開されていたのですが、肝心の実行環境であるWindows 10評価版用のWDDM 2.0対応ドライバーがWindows Update経由でしか取得できず、また新しいWUはプロキシ経由で正常に動作しない現象が出ていたため、Direct3D 12の評価はしばらく見送っていました。
その後、4月末にWindows 10 Insider Preview Build 10074、Visual Studio 2015 RC、そしてWindows 10向けのNVIDIA GeForceベータ ドライバーのスタンドアロン版が相次いでリリースされたため、Direct3D 12の検証を再開することにしました。

日本語版Windows 10 Build 10074 (64bit) 上でdxdiagを実行してみたところ、確かにWDDM 2.0対応になっているようです*1。検証に使ったグラフィックスカードはNVIDIA GeForce GTX 770 4GB版です*2
https://sygh-jp.github.io/content_hosting/software_ss/windows10ip_build10074_dxdiag_ss01.png

Visual Studio 2015 RCでDirectX 12プログラミングを行なう場合、インストール時に[ユニバーサル Windows アプリ開発 ツール] - [ツールおよび Windows SDK 10.0.10069]にチェックを入れておく必要があります。デフォルトの設定ではインストールされないので、初回インストール時にチェックし忘れていた場合は、[プログラムと機能]からVisual Studioの更新インストール機能を使って追加します。今回はデスクトップ アプリ上でさくっとテストしたかったので、フレームワークにはおなじみのMFCを使っていますが、MFCもデフォルト設定ではインストールされないので、[Microsoft Foundation Classes for C++]にもチェックを入れておきます。
https://sygh-jp.github.io/content_hosting/software_ss/vs2015rc_setup_mfc_sdk10_ss.png

サンプルプログラムは下記です。MSDNの暫定ドキュメント「Creating a basic Direct3D 12 component」に書かれてある断片コードを参考にしていますが、現時点では暫定だけあって実際のSDK実装と微妙に異なっていたりするので苦労しました。また、Windows SDK 8.0で廃止されたと思われていたD3DXが、今度はC++ヘッダーベースのヘルパーとして復活するようですが、Visual Studio 2015 RCにはまだ <d3dx12.h> は含まれていないようでした*3

https://sygh-jp.github.io/content_hosting/my_program_ss/MyMfcSimpleD3D12Test1_ss_2015_05_14a.png

※2017-01-09追記:
サンプルのプロジェクトファイルをVisual Studio 2015 Update 3にてビルドできるように修正しました。Windows 10 November 2015 Update (version 1511, build 10586) とNVIDIA GeForceドライバー358.91にて動作確認しています。

今回はとりあえずD3D12コアAPIの雰囲気をつかむため、トライアングルを描画するだけの簡単なコードにしました。ただしPipeline State Object (PSO) の切り替え方法(ID3D12GraphicsCommandList::SetPipelineState()メソッド)をテストするために、下記2つのGraphics PSOを作成して、複数描画しています。

  • 図形の回転なし、ブレンディングなし
  • 図形の回転あり、ブレンディングあり

今後はコンピュート機能の非同期実行効率がどうなっているのか、またWindowsストアアプリ(XAML連携)やDirect2D/DirectWriteとの相互運用に関しても調査する予定です。どうもD3D12デバイスからはDXGIデバイスインターフェイスが取得できなくなっているため、D3D11デバイスとの相互運用機能(D3D11On12CreateDevice()関数など)を経由する必要があるようです。

Direct3D 12 APIを触ってみた感想

今回のサンプルの目的自体は単純とはいえ、Direct3D 12はMantleのようにハードウェアに近いローレベルAPIを標榜しているだけあって、ユーザーコードの記述量はDirect3D 11までと比べて格段に増えています。特に難しいと感じたのがDescriptor Heapまわりです。一方、Direct3D 11ではID3D11VertexShader, ID3D11PixelShaderや、ID3D11Buffer, ID3D11Texture2Dなどのようにシェーダーステージやリソースの種類ごとに細かくインターフェイスが分かれていたのですが、Direct3D 12ではID3D12PipelineStateやID3D12Resourceのように統合・統一され、結果としてCOMインターフェイスの数はD3D11よりも少なくなっています。これによってD3D12ではオブジェクトやリソースの初期化が統一的に行なえるようになっているのですが、ID3D12Device::CreateGraphicsPipelineState()やID3D12Device::CreateCommittedResource()といった生成メソッドはパラメータ(に使う構造体のメンバー)が非常に多くなっていて大変です。また、これまでAPIおよびドライバー層が面倒をみてくれていたCPUとGPUの同期も、ユーザーコードで明示的に記述する必要があります。

なおDirect3D 12 with Visual Studio 2015 RCのサンプルとして、いくつかの実践的なテストプログラムを下記GitHubにアップロードされている日本人の方がいます。こちらも参考になるでしょう。

Direct3D 10以降、固定機能シェーダーが廃止されてプログラマブルシェーダーが必須となり、Direct3D 9のようなお手軽感はなくなっていたものの、単に3Dグラフィックスを描画するだけであれば、アプリケーションのコーディングが死ぬほど大変というほどのものではありませんでした。ハードウェアの限界性能を引き出して最大限最適化しなければならないというような厳しい要求がなければ、Direct3D 11は十分に完成形と言えるAPIで、Direct3D 9ユーザーでも時間をかければ確実に習得して使いこなせる易しいAPIだと思います。しかし、Direct3D 12は実行効率の向上を至上目的とした、完全に玄人向けのAPIです。ゲームエンジン設計者ならばいざしらず、純粋にアプリケーションコードのほうに注力したいであろう標準的なグラフィックスプログラマーが手を出せるシロモノではありません。おそらくVulkanもDirectX 12同様の玄人志向APIとなることでしょう。UnityやUnreal EngineはすでにDirectX 12への対応を表明しているため、単に3Dグラフィックス プログラミングをやりたいだけであれば、そういったミドルウェアを利用するか、DirectX 11.xやOpenGL/OpenGL ESを使い続けるのが賢明だと思います。

Windows 10に思うこと

先日、Windows 10が最後のWindowsメジャーバージョンアップとなることがマイクロソフトによって正式に言及されましたが、それにしてはOSの完成度が異様に低いように感じます。まだ評価版の段階ではありますが、7月末リリースを目標としているらしい状況にもかかわらず、現時点でGUIの設計にかなり粗が目立ちます。自分はPC主体のWindowsユーザーですが、ぶっちゃけ全体的にWindows 8.1のほうが使いやすいですし、タイトルバーやフォント、アイコンのデザインも8.xのほうが良いと思います。現状、Windows 10のタイトルバーはアライメントや配色のバランスが悪くて安っぽく、また日本語版の新たなシステムフォントとして使われている細身のYu Gothic UIフォントはClearTypeの調整不足もあるせいか貧弱・低品質な印象を受けます。ていうかおためごかしにフォントを変えるよりも、まずフォントレンダリングエンジンの品質をAppleAdobe並に強化するべきでしょう。また特に今回はタスクバーまわりに頻繁に手を入れているらしく、最もユーザーが使う機能でありながら評価版ではバグの温床となっているように見受けられます。そのほか、Windows 7で機能的にも充実し、完成形と思われていた、おなじみのデスクトップ版の電卓アプリ(calc.exe)が廃止され、大味なストアアプリ版の電卓アプリに一本化されたのも地味に悲しいです。

DirectX 12はおそらくWindows 7/8.xにはバックポートされず、結局Windows 10以降専用の機能となるように思われるのですが、最終製品版ではもっともっとOS全体の完成度を上げてくれないと、DirectX 12のためにアップグレードする気には到底なれません。
それにしてもフィードバックは専用のInsider Programに参加させるのではなく、MS Connect経由でも提出できればいいのに……

*1:ちなみにWindows 10上でもWindows 7/8/8.1向けのWDDM 1.1/1.2/1.3ドライバーを使うことはできるようです。2014-07-29にリリースされたNVIDIAの安定ドライバー340.52をインストールしたWindows 8Windows 10 TPにアップグレードすると、WDDM 1.3になっていました。ただしWDDM 1.x環境ではD3D12を使用することはできず、D3D12CreateDevice()関数がDXGIエラー (DXGI_ERROR_UNSUPPORTED, 0x887A0004) で失敗します。

*2:Kepler世代はいずれも機能レベル11_0になります。

*3:Direct3D 12ドキュメントの正式版「Helper Structures and Functions for D3D12」によると、d3dx12.hはGitHubにて補助ライブラリとして分割管理されているようです。また、正式版のVisual Studio 2015に含まれているDirectX 12のUWPアプリケーションプロジェクトテンプレートにも、d3dx12.hが含まれているようです。

GPGPU戦争の歴史を紐解く ―勃発から現在に至るまで―

OpenCL CUDA GPU DirectX

GDC 2015でついにOpenCL 2.1が発表されました。SPIR-V中間表現(中間言語バイトコード)でプログラミング基盤をVulkan API (OpenGL Next Generation, glNext) と共有することができるそうです。これでカーネルをオフラインコンパイルできるようになります。OpenCL 1.2/2.0にはもともとSPIR 1.2/2.0という中間言語がすでに存在していたのですが、コア機能ではなく拡張機能扱いで、ベンダーごとに対応がまちまちでした。SPIR-Vは晴れてOpenCLのコア機能となります。個人的にはカーネル記述言語にC++14ベースが導入されることになったのが嬉しいですね。テンプレートやラムダ式が使えます(特にテンプレートは渇望していた機能)。というかこれまでのC99/C11ベースのOpenCL-Cはあまりにプログラマビリティが低すぎました。CUDAはもちろんHLSLにも後れを取る始末で、正直GLSLと大差ありません。OpenCLカーネルにおけるC++採用は、C言語がすでにアプリケーション記述の主力言語としては時代遅れとなっていることを象徴しているのではないでしょうか。

そのついでといってはなんですが、今日はGPGPUの発展・普及の歴史の中でも最大の功労者達と思われるAMD (ATI) とNVIDIAの戦いのうち、まずはDirectX 10出現の前後からDirectX 11 (DirectCompute) 出現までの時間軸におけるデファクトスタンダードの存亡をかけた攻防戦を見ていくことにします。

DirectX 10世代から11世代に至るまでのお話

2015年現在、GPGPU基盤技術として知名度が高いのは圧倒的にNVIDIA CUDAのほうで、AMD StreamとかAMD APP (AMD Accelerated Parallel Processing) とか何それ食えるの?という有様です。後から出てきたDirectComputeにすら負けています。

しかし実はATI (AMD) のほうがNVIDIAよりもGPGPU対応製品の投入時期が先だったとか倍精度対応が先だったとかいう情報があります。
前々から真偽のほどが気になっていたので、その裏付けをやってみることにします*1

GPGPU対応製品の投入

ATIRadeon X1900(Direct3D 9世代、統合型シェーダーアーキテクチャではない)上でのGPGPU基盤技術として2006年10月に「Stream Computing」を実現・実用化しているので、確かにNVIDIA CUDA(2006年11月発表、2007年7月リリース、統合型シェーダーアーキテクチャ必須)よりも先になる。
ATI Stream Computingの発表は、奇しくも統合型シェーダーアーキテクチャを初めて採用したXbox 360Xenos供給開始後。

ちなみにAMDによるATI買収は2006年10月に完了。買収騒ぎでドタバタしている中、技術者達は普段通り頑張っていたらしい。

しかしAMD FireStream 9170よりも前のストリームプロセッシング(GPGPU)向け製品(第1世代)が見つからない。英語版Wikipediaの情報が確かならば、ATIの初代GPGPU向け製品はRadeon X1900ベースのFireStream 2U (R580) とかいうヤツらしい?
(ちなみに日本語版のWikipediaはほぼ英語版のコピーで、たいした情報はない)

しかし「FireStream 2U」で検索しても、いつリリースされたのかまったく不明。オークションや中古品の取引情報以外はまともな情報がまったく引っかからない。

実は初代FireStreamは「AMD Stream Processor」という名称だったらしい。2006年11月発表。この子を誰か覚えていますか……

一方、G80ベースの初代NVIDIA Tesla C870は2007年6月発表。確かにGPGPU向けデバイスを発表したのはAMDのほうが先(Direct3D 10とCUDAに初めて対応したGeForce 8800は2006年11月発表だが、GPGPU向け製品ではない)。

倍精度対応

ゲームのようなリアルタイム3Dグラフィックスでは倍精度浮動小数点が必要な場面はほとんどなく、特にGPUが担当する画面表示に関しては単精度浮動小数点ですべてまかなえる(CADに関しては話は別だが、内部演算精度と画面表示精度はまた別物)。
一方、科学技術計算においては、倍精度対応は必須であり、倍精度対応の出来が汎用計算に使えるかどうかの分かれ目になる。

調べてみたところ、倍精度対応も確かにNVIDIA Tesla C1060(2008年7月発表)よりAMD FireStream 9170(2007年11月発表)が先らしい。

しかもTesla C1060は単精度に比べて倍精度演算がすさまじく遅かった。それもそのはず、倍精度演算ユニットはSM (streaming multiprocessor: 複数のCUDAコアを束ねるユニット) に対して1個だけであり、まともに積んでいない。
NVIDIAが本気で倍精度対応するのはFermi世代から*2。ただしハーフレートの倍精度を実装したのは後にも先にもFermiのみで、その後にリリースされたKepler、Maxwellでは倍精度のサポートが削られています。

余談:その名はCUDA ―APIの使いやすさと普及性―

下記は古い記事だが、医療分野でCUDAを利用したアプリケーション開発者へのインタビュー(特にPS3のCellやAMD/ATIのClose To Metalとのプログラマビリティ比較に関する所見)が興味深い。
結局のところ、最後はソフトウェアの組みやすい環境へとアプリケーション開発者は流れるもの。
いくら良いハードウェアを作っていても、ソフトウェア開発技術の発展と普及推進、およびそのための基盤整備を怠ったことがATI/AMDの敗因だったといえる。

AMDGPGPUテクノロジー(API)は、Close To MetalやATI Stream/AMD Stream、CALやBrook+言語というわけのわからない(というか総じて馴染みの薄い)キーワードばかりが羅列され、ニューカマーには実態や構造がいまいちよく分からず、さらに現在はAMD APPとしてひとくくりに包括されてしまっている始末。
たぶんAMDの広報担当ですら、すべて正確に把握できている人間はいないのではないかと。
このあたりのブランド戦略の一貫性のなさも、GPGPU/HPCにおけるAMD劣勢の一因だと思われる。とにかくAMDGPGPUはふらふらしていて足元がおぼつかない。

NVIDIAがCUDAやTeslaという用語やブランドを継続して使用しているのとは対照的。CUDAのネームバリューはすさまじく、今はとりあえず「CUDA!!」と言っておけば大抵なんでも通じる。技術的知識がない人も「CUDA!!」でだませる。あと固有名詞なので、Web検索性が高く、狙った情報が手に入りやすい*3

そしてOpenCL

実際にはもっと熱くて濃ゆい、漢(オトコ)の戦いが繰り広げられていたと思うんですが、今回のツッコミというか検証はここまでにします。

現在のAMDはとりあえず独自API路線はやめてOpenCL推進に落ち着き、開発ツールの整備(CodeXL)や2.0規格のサポートも進めている模様(しかし肝心の対応SDKは2015年3月時点でいまだにベータ)。

今後のAMDOpenCLと命運をともにすることになると思われます。
高レベルAPIであるC++ AMPに関しても、Microsoftと協力してLinux移植を進めるなど、わりと意欲的です(OpenACCを開発したPGIを買収したNVIDIAに対する牽制の意味もあるのかもしれません)。
ちなみにJavaコンパイラをHSA環境向けの中間言語HSAILに対応させて、JavaGPGPUプログラミングできるようにする計画もあるそうです。C++ AMP相当の拡張をJavaに導入する感じになるんじゃないでしょうか。

あとFireStreamは2015年現在、FireProブランドに統一されていて、FirePro Sシリーズとなっています。名前変えすぎです。どおりでFireStreamで検索してもPCI-e 2.0世代の旧製品しか引っかからないわけだ……*4

結論としては、

  • ATI (AMD) が実はGPGPUの先駆者だったという伝説はデマではなく確からしい。
  • 現在のAMD環境向けGPGPUアプリケーション開発者はとりあえずOpenCL、DirectComputeもしくはC++ AMPを使っとけ(AMD独自技術はNG)。
  • ローレベル技術(ハードウェア層)には手を出すな。十分な開発情報が得られないだけでなく、すぐに廃れて過去の遺物になるだけ。もしどうしても使う気ならばそれを覚悟するべし。

なおローレベルAPIといえばMantleですが、Vulkan APIの要素技術として取り込まれることになったとはいえ、単独のMantleの今後はどうも怪しい感じです。

*1:ちなみに自分がGPGPUに取り組み始めたのは、DirectX 11対応のNVIDIA製品すなわちFermiがリリースされた後だったりします。なので「GPUでもHPC向けだったら倍精度対応とか当たり前じゃん」って感じですね。むしろ倍精度に対応してない時代があったことのほうが信じられない。

*2:FermiのTeslaといえばGTC会場においてでっち上げられたモック(ハリボテ)が有名。

*3:AMDのほうは、BrookはまだしもClose To MetalとかStreamとか一般名詞や形容詞の組み合わせだったりするのでWeb検索性が異様に悪いです。AMDという社名自体もWeb検索性が悪く、Google検索では「AMD」で検索すると「and」もついでにハイライトされるのでノイズが増えます。

*4:グラフィックスカード製品はあっという間に時代遅れの骨董品になります。その速度たるやCPUのそれを凌ぐレベルで、それだけ技術の進歩・進化が激しい分野だと言えます。

NVIDIA/AMDのDirectX 11.x/12対応に関して

Direct3D DirectX 技術・テクノロジー GPU

NVIDIAの場合

GeForce GTX 970のメモリ3.5GB問題で、嘘スペック(なんちゃって4GB他)を公表していたがために訴訟を起こされてしまったNVIDIAですが、実はDirectX (Direct3D) 対応レベルに関してもわりとあこぎなことをやっているというか実際詐欺です。

Fermi/Kepler/Maxwell世代でDirectX 12に対応するとの報道がありますが、少なくともFermi/Keplerに関してはGTX TITAN Zですら機能レベル (Feature Level) 11_0にとどまっており、「DirectX 11.1対応」や「DirectX 11.2対応」を謳っている製品でも実際にフルサポートしているのはDirectX 11.0までの機能となっています。

ASUSやEVGAはきちんとFeature Levelに言及していて正直なんですが、NVIDIA Japan公式サイトの780/750/Titan Blackはアウトですね。980のDirectX対応表記も「12 API」となっていますが、肝心の機能レベルへの言及がありません。ELSAは「Shader Model 5.0」と表記しており、たぶんDirectX 11.0までの対応だと言いたかったんだと思いますが、実はShader Model 5.1仕様に関してMicrosoftからの公式情報はありません(MSDNDirect3D APIヘルプには一切書かれていないし、Windows SDK 8.0/8.1付属のシェーダーコンパイラーfxc.exeにもvs_5_1やps_5_1のようなプロファイルは存在しない)。DirectX 11.1/11.2であってもShader Model自体は5.0のままであり、ELSAが想定しているであろうShader Model 5.1というのは幻です。

なんでこんなわけの分からないことになっているのかというと、Fermi/KeplerはドライバーレベルでDirectX 11.1やDirectX 11.2のAPI自体はサポートしていて、またDirectX 11.1の機能のうち実際にいくつかはサポートしているんですが、すべてではないため、Direct3D 11 APIを使ってデバイスの機能レベルを取得すると11_1 (D3D_FEATURE_LEVEL_11_1) ではなく11_0 (D3D_FEATURE_LEVEL_11_0) となります。これに関しては実際に手元にあるKepler世代のGTX 760や770で試したことがあり、実証済みです。

DirectX 11.1のCapsは下記構造体です。

DirectX 11.2のCapsは下記構造体です。末尾の数字が2じゃなくて1なのがまぎらわしくてウザい感じです。

ちなみにD3D_FEATURE_LEVEL_11_2はありませんのであしからず。なおDirect3D 11.1/11.2の各機能対応状況は、Windows SDK 8.1付属のDirectX Caps Viewer (dxcapsviewer.exe) で確認することもできます。またGPU-Zの最新版を使うことで、DirectXのフルサポートバージョンを簡単に調べることができます。

DirectX 10世代の古いカード(G80)とかでも最新版の対応ドライバーをインストールすることで、Windows Vista SP2 Platform Update/Windows 7以降であればDirectX 11.0 API自体はFeature Level 10_0で使えますよね? それと一緒です。下記の公式ブログにおける「all Fermi, Kepler, and Maxwell GPUs will fully support the DX12 API」という表現も、そういう意味を含めたうえでの発言でしょう(おそらくDirectX 12によるマルチスレッドレンダリングやコンピュートタスクの効率向上はFermi/Keplerでも享受できるはず)。

DirectX 12およびDirectX 11.3に関しては先行プログラムに参加しているベンダーや開発者のみが詳細を知っている状態で、まだ一般公開されていないステータスであるためはっきりとしたことは言えませんが、GTX 900シリーズ(第2世代Maxwell)に関しては少なくともDirectX 12/DirectX 11.3の新機能のうちいくつかはサポートしているよ、とNVIDIAの中の人がフォーラムで言及しているようです。機能レベルがどうなるのかは不明です。

2015-03-23追記:
GDC 2015で発表された情報によると、DirectX 12では12_0および12_1という機能レベルが実装されるそうです(スライドではFeature Level 12.0および12.1と表記されています)。

個人的に特に残念なのは、ROV (Rasterizer Ordered View) が確実に使えるのは12_1のほうで、12_0以下では使えない(オプション扱い)という点でしょうか。早くも格差が生まれてしまったという悲壮感が漂います。

ちなみに英語版Wikipediaの「Direct3D」の記事には、「AMD GCN 1.1/1.2では機能レベル12_0となる」「NVIDIA GeForce 900シリーズ(TITAN Xも含む)では機能レベル12_1となる」と2015年3月時点で記載されているんですが、DirectX 11.3/DirectX 12およびWindows 10の正式版はまだリリースされておらず、MSDNライブラリの正式ドキュメントすら準備されていない現状、機能レベルに関する引用はChannel 9のビデオだけで、かなりフライングしています。
マイクロソフトおよびハードウェアベンダー(メーカー)からの最終的な正式情報(特に文字情報の検索が可能なドキュメント)を待つべきだと思うんですが……

2015-04-08追記:
Windows 10 Technical Preview上でVisual Studio 2015 Preview付属のWindows SDK 10を用いることで、一般の開発者でもDirect3D 11.3およびDirect3D 12を使えるようになっているようで、各々の暫定ドキュメントがMSDNライブラリにて公開されています。ただし実行にはWDDM 2.0対応ドライバーを導入する必要があります。最終的な仕様はまた変更される可能性はありますが、大枠はすでに固まっているものと思われます。

DirectX 11.3のCapsは下記構造体です。

DirectX 12のCapsは下記構造体です。

今回もD3D_FEATURE_LEVEL列挙体のメンバーに変動はなさそうです。

2015-05-12追記:
2015年4月末にリリースされたVisual Studio 2015 RC付属のWindows SDK 10では、D3D_FEATURE_LEVEL_12_0およびD3D_FEATURE_LEVEL_12_1が追加されているようです。また、同SDK付属のシェーダーコンパイラーfxc.exeには、幻と思われていたシェーダーモデル5.1のプロファイル(cs_5_1, ds_5_1, gs_5_1, hs_5_1, ps_5_1, vs_5_1)が実装されているようです。シェーダーモデル5.1自体はDirect3D 12 APIをドライバーレベルでサポートするハードウェアであれば、D3D12/D3D11 APIを通して使用可能らしいですが、5.1で新しく追加されたROVに関するオブジェクト(RasterizerOrderedBufferほか)は、ROVに対応するハードウェアのみで使用可能となるはずです。

AMDの場合

AMDに関しては、GCNでFeature Level 11_1をフルサポートするという話だったんですが、結局どうなっているんでしょうか?

また、Radeon HD 7000シリーズにおけるDirectX 11.2に関しても、ドライバーレベルで対応可能という話だったんですが、これも実際のところどうなっているんでしょうか?

ASUSによると「DirectX 11.1対応」「DirectX 11.2対応」とかいいつつ実はFeature Level 11_0どまりとなっています。ASUS JAPANのサイトが更新をサボっているだけ?

SAPPHIREのサイトでは「GCNベース製品はすべてDirectX 12対応予定」と書かれているだけで、Feature Levelへの言及はありません。
NVIDIA同様、最終的にDirectX 12 API自体の対応がなされるだけで、旧製品に関してはFeature Level 12_0/12_1ではなくなるのでは? 新型Radeonに搭載される予定のFijiあたりは12_1にフル対応してきそうですが……

結論

問題は何をもって「11.1対応」「11.2対応」「12対応」と称するべきか、なんですが、個人的にはまともにフル機能をサポートしていない中途半端な製品なのに「DirectX ○○対応!!」とかいう詐欺商法は即刻やめて欲しいです。エンドユーザーがガッカリするのはもちろんのこと、開発者もCaps絡みで苦しむだけです。個別の機能が使えるかどうかいちいちフラグをチェックする必要があるなんていう三流仕様はOpenGLだけにしてください。ていうかDirectX機能レベルというものの存在に関して、ベンダーはもっと説明責任を果たすべきでしょう。かつてDirect3D 9全盛期の頃、グラフィックスカードのベンダー各社はDirectX 9.0c対応を謳いつつも、浮動小数バッファのアンチエイリアスATIだけがサポート、VTFはNVIDIAだけがサポート、というようなひどい状況になっていたことがありましたが、そういった細かい機能のサポート有無は非常に重要な情報であるにもかかわらずスペックシートには一切書かれていませんでした。

2015-06-03追記:
現行のGeForceシリーズにおけるDirectX機能レベル対応度に関しては、NVIDIAからGeForce GTX 980 Tiとともに公式の解説が発表されました。Maxwell第2世代以降でFeature Level 12_0と12_1にフル対応するようです。当該ハードウェアを所持していて、Windows 10 Preview BuildとWDDM 2.0対応ドライバーをインストールできる人は実際に試してみるとよいでしょう。

AMDも新型Radeonシリーズの発表の際には、GCNアーキテクチャの世代とDirectX機能レベルに関して言及してくるものと思われます。

※2015-06-21追記:
The Era of PC Gamingで発表されたように、新しいRadeonはR9 Furyシリーズ(R9 Fury X、R9 Fury、R9 Nano)のみがFiji(GCN 1.2? 1.3?)を搭載するようです。R9 380/390/390Xのほうはすべてリネーム(リブランド)で、HawaiiやTongaなど(GCN 1.1/1.2)を採用するものと思われます。機能レベルに関する言及はありません。

科学技術英文を翻訳するコツ

技術・テクノロジー 英語

仕事柄、ネイティブ英語圏の開発者と英文メールをやりとりすることがあるんですが、ときにはローカライズ作業などで英文を日本語に翻訳しなければならないこともあります。もちろんその逆もあります。

自分は技術者なので、先方とやりとりする英文の文法自体は特段難しいものではなく、専門用語や技術用語(テクニカルターム)が多数出現することを除けばむしろヘタなビジネス英語よりも易しいくらいですが、技術文書には英語・日本語を問わず特有のクセや言い回しがあります。
日本語として自然な文章になるよう翻訳したいときは、下記のようなサイトが参考になると思います。読み物としても面白く、特に翻訳の際に受動態・能動態をどう使い分けるか、また無生物主語の扱いをどうするかという話は必見です*1

翻訳の泉 - 教室

逆にいえば、自然な英文を書く際にも、こういった日本語への翻訳に関する知識は役に立ちます。受験英語から一歩進んだWritingのテクニックを勉強しておくことは大いに意義があるでしょう。英語圏のネイティブ向けに、日本人が英語でマニュアルや仕様書を書かなければならない場合、下記のような資料も参考になります。

ドキュメンテーション(文書化)の基礎知識【講議用テキスト】株式会社 テクライト・ジャパン ドキュメンテーションチーム

*1:日本語ではもともと主語を省略することが多いのですが、そのせいで日本人にとっては無生物主語という存在が奇異に映るのかもしれません。

NVIDIAのOpenCL対応状況

C++ OpenCL プログラミングTips GPU CUDA Direct3D DirectX

NVIDIAOpenCLドライバーはIntelAMDと比べて規格への対応が遅く、2015年2月時点でもOpenCL 1.1どまりです。NVIDIAOpenCL SDKとなるCUDA Toolkitも、7.0時点でOpenCL 1.1までしか対応しません(cl.hに定義されているのはCL_VERSION_1_1まで)。例えばOpenCL 1.2のimage3d書き込み拡張機能や、OpenCL 2.0のread_write修飾子には対応していません。

英語版 Wikipedia (2015年3月閲覧) には、Kepler/Maxwell 世代の GeForceOpenCL 1.2 に対応していると記載されているんですが、どうもこれは Linux に限定した話のように思われます。

いくつかのドライバーバージョンを Fermi/Kepler 世代の GPU とともに試してみましたが、少なくとも Windows の 340.62、344.11、347.25、347.88 ドライバーでは、NVIDIAOpenCL プラットフォーム バージョン (CL_PLATFORM_VERSION) は 1.1 どまりです。なお NVIDIA のドライバーにはベータ ドライバー、開発者向けドライバー、エンドユーザー向け安定ドライバーなど、いくつかのブランチがあるため、単純に数字が大きいほうが新しいとは限りません。
ちなみに Linux 用のドライバーは Windows 版と違ってまともなリリースノートがなく、したがって対応する OpenGL/OpenCL バージョンが記載されているようなまともなドキュメントが存在しない模様。

英語版 Wikipedia で引用されている下記のデータベース サイトも信憑性が疑われます(個人的には DirectX の対応バージョンに Feature Level すなわち機能レベルが記載されていない時点で、すでにもう信用ならない)。

※2015-05-05追記:
Windows版ドライバーもバージョン350.12でOpenCL 1.2に正式対応した模様です。ただしKeplerおよびMaxwell世代のGeForceのみで、QuadroおよびTeslaはまだ対応していません。またFermiはKepler同様にDirectX 11対応ですがOpenCL 1.1どまりとなるようです。なお、OpenCL 1.2拡張のcl_khr_spirおよびcl_khr_3d_image_writesはサポートされないようです。ちなみにCUDA Toolkitのほうは7.0時点でOpenCL 1.2に対応していません。OpenCL 1.2のヘッダーはKhronosのサイトからダウンロードするとして、新しい関数エントリポイントはどうやって取得するんでしょうか? LoadLibrary()+GetProcAddress()ではないだろうし、clGetExtensionFunctionAddress()も違うだろうし……OpenCL.dllにエクスポートされているのはOpenCL 1.0の関数のみであり、またOpenCLにはInstallable Client Driver (ICD) Loaderによって、OpenCL APIコールを特定のプラットフォーム実装にディスパッチする仕組みがあるんですが、Khronosが公開しているICD Loaderのtgzパッケージにはdefファイルが含まれていました。これを使ってlibファイルを作って遅延バインドしろということなんでしょうか……OpenCL 1.2を使いたい場合、ターゲット環境がNVIDIAであってもIntelあるいはAMDSDKを使って開発したほうがいいかもしれません。

※2015-07-10追記:
Quadro/Teslaも353.06ドライバーでOpenCL 1.2に正式対応したようです。こちらもKeplerおよびMaxwellのみとなります。

※2015-12-30追記:
CUDA Toolkitも7.5でOpenCL 1.2に正式対応したようです。リリースノートには何も書かれていませんが、cl.hにはCL_VERSION_1_2が定義されるようになったので、OpenCL 1.1までしか対応していないFermi世代以前の旧製品や旧ドライバーでも実行可能なOpenCLアプリケーションを開発する場合は注意が必要です。なお、CUDA Toolkit 7.5.18付属のOpenCL 1.2ドライバーには致命的なバグがあり、clRetainDeviceなどのOpenCL 1.2関数エントリポイントをICDローダー経由で正常に取得できない現象が発生します。

OpenCL-C コンパイラーの挙動

OpenCL 対応が 1.1 までというのは GeForce ドライバーでも Quadro ドライバーでも同じなのですが、今回調査してみた結果、ドライバーによって OpenCL-C コンパイル エラー発生時の挙動がずいぶん異なるようです。

具体的には、例えば下記のカーネルコンパイルする際、Quadro ドライバーだと read_write 周辺できちんとコンパイル エラーになるものの、GeForce ドライバーだとコンパイル エラーというよりは PTX アセンブル エラー*1になるようです。

const sampler_t myLinearSamplerObj = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP | CLK_FILTER_LINEAR;

__kernel void myOclkDoProc(
  __read_write image2d_t inoutImage
)
{
  // For Compile Test
  const float4 oldValue = read_imagef(inoutImage, myLinearSamplerObj, (float2)(0, 0));
  write_imagef(inoutImage, (int2)(0, 0), oldValue + (float4)(1, 0, 0, 0));
}

結果(CL_PROGRAM_BUILD_LOG):

[Driver 340.62, on Quadro 2000]
:RRR:CCC: error: unknown type name '__read_write'

Quadro では、clBuildProgram()CL_BUILD_PROGRAM_FAILURE で失敗します。これは想定範囲内。

一方、GeForce のほうは OpenCL-C 自体のコンパイルは通ってしまいます。とはいえ、最終的にアセンブル エラーが発生してしまうため、カーネルを動作させることはできません。しかも read_write を指定したオブジェクトに対する read_imagef() 呼び出しがアセンブル エラーのトリガーになるようで、write_imagef() はエラーにならないようです。

[Driver 347.25, on GeForce GTX 770]
ptxas application ptx input, line XXX; error   : Argument 1 of instruction 'tex':
 .texref or .u64 register expected
ptxas fatal   : Ptx assembly aborted due to errors

clBuildProgram()CL_INVALID_BINARY で失敗します。

OpenCL 1.1 コンパイラーとして正しい挙動なのは Quadro ドライバー実装のほうです。GeForce はひどすぎです。ハードウェアやプラットフォームの違いを吸収するのが OpenCL のような標準 API に与えられた役目であるはずなのに、いきなり PTX アセンブル エラーとか言われてもなんだよそんなの知らねーよって感じです。

こういったドライバーの出来不出来の差というのは何も OpenCL ドライバーに始まったわけではなく、OpenGL ドライバーにも古くから存在します。中にはそもそも GeForce ではサポートされず、Quadro でしかサポートされない GL ベンダー拡張もあったりするのでさらに厄介です。自分が現行の OpenCLOpenGL/GLSL を毛嫌いしているのは、このようにハードウェア ベンダーに好き勝手を許してしまっている無責任でいい加減な規格だからです。よく言われるポータビリティや互換性というのは無知な開発者をだます甘言でしかありません。Twitterでは布教も兼ねてしょっちゅうdisっています。

そもそも公式のオフライン コンパイラーもまともに用意されていない三流の仕様を、アクセラレータ系 API の標準規格として認めている時点でどうかしています(nvcc や fxc に相当するものがないのは致命的すぎる)。なぜ OpenCL-C 規格を作る前にバイトコード規格(中間言語)を作らないのかと。最初にバイトコード規格を作っておけば C 以外でもカーネルコードを生成できる独自言語を自由に開発できる可能性も出てくるんですが、いきなり高級言語の規格を作って実装をベンダーに投げようとするあたり本当に頭がおかしいです。ちなみに OpenCL中間言語 SPIR は後出しの規格で、SPIR 1.0 の暫定規格は 2012-08-24 に発行されています。現行規格では OpenCL 1.2 の SPIR 1.2、そして OpenCL 2.0 の SPIR 2.0 が存在しますが、それぞれ必須サポート機能ではなく拡張機能扱い(cl_khr_spir)でしかないのが非常に残念です。もともと OpenCL を提唱したのは Apple なんですが、OpenCL 1.0 時点で相当に見積りの甘い API 設計だったとしか言いようがありません。CUDA や DirectCompute と比較するとおもちゃレベルです。

OpenCL 1.2, 2.0 対応に関して

OpenCL 1.2 の image3d 書き込みと OpenCL 2.0 の read_write 修飾子は、CUDA の SurfaceDirect3D/HLSL (DirectCompute) の RWTexture に近い機能で、DirectX 11 世代の GPU であれば普通にハードウェア レベルでサポートしているはずの機能ですが、ドライバーが対応していなければ意味がありません。この write_image(image3d_t) と read_write がないと、CUDA プログラムや HLSL コンピュート シェーダープログラムを OpenCL に移植するのに結構困ります*2。しかも image3d 書き込みに至っては標準機能ですらなく、ただの拡張機能扱いです(cl_khr_3d_image_writes)。GPGPU においては基本中の基本と思われる重要機能を拡張扱いにするなど、Khronos の連中がいったい何を考えているのかもうさっぱり分かりません。

OpenCL 拡張に関する不満はとりあえずおいておくとして、現時点では少なくとも OpenCL 2.0 が使えるかどうかというのは移植性の観点からも非常に重要になってくるんですが、2015年2月現時点での最新規格である OpenCL 2.0 をサポートしているのは「AMD APP SDK 3.0 Beta」と「Intel SDK for OpenCL Applications 2014」くらいです。
AMD は GCN アーキテクチャ(GCN 1.1世代以降)の GPU/APU で OpenCL 2.0 をハードウェア サポートしているものの、対応ドライバーの RC 版がリリースされたのは2014年9月になってからで、さらに SDK が例のごとく未だベータ ステータスということからも分かるように、正直ドライバー品質に関しては信頼できません。OpenCL 2.0 規格発表は 2013-11-18 なんですが、同規格にようやく対応した AMD APP SDK 3.0 Beta の発表は 2014-12-09 です。正直対応が遅すぎです。1年もかけていながら、お前らいったい何をやっていたのかと。
http://developer.amd.com/community/blog/2014/12/09/amd-app-sdk-3-0-beta/

なお OpenCL 2.0 対応の AMD ドライバーは、Windows に関しては現状 Win8.1 x64 のみサポートです。Win7 向けには提供されていません。マルチ GPU もサポートしてないらしいです(CrossFire 無効で AMD グラフィックスカードを2基接続しても、GPGPU に使えるのは1基だけ?)。
http://support.amd.com/en-us/kb-articles/Pages/OpenCL2-Driver.aspx

ちなみに AMDOpenCL のソフトウェア エミュレーターオープンソース開発しているようですが、1.2 どまりです。2.0 には対応していません。開発も2012年10月を最後に停滞していて、相変わらず仕事が中途半端な印象の AMD です。目の付けどころは悪くないんですが……
http://developer.amd.com/tools-and-sdks/opencl-zone/opencl-emulator-debugger/

IntelOpenCL 2.0 をハードウェア サポートしているのは最新世代(Broadwell アーキテクチャ)のモバイル プロセッサーである Core M などのみとなっています。
http://www.isus.jp/article/intel-software-dev-products/opencl-sdk/
https://software.intel.com/en-us/intel-opencl

そして哀しいかな、HPC 分野でも、すでに OpenCL には見切りをつけて冷ややかな目を向け始めているとのこと。
http://news.mynavi.jp/articles/2014/12/16/gpu_mic/003.html

正直な話、現時点で GPGPU を本気でやろうとするのであれば、OpenCL より CUDA や DirectCompute を選択することをお勧めします*3。ベンダーロックイン? 知ったこっちゃないですね。低品質のドライバーのデバッグに付き合わされるよりは遥かにマシでしょう。とはいえ個人的には、NVIDIAOpenCL に対するスタンスが気に食わないのは確かです。ベンダーロックインによる囲い込みばかりを重視するあまり、標準規格の推進・強化・準拠がおろそかになっています。邪推ですが OpenCL のサポートは意図的に手を抜いているんじゃないかと。CUDA が NVIDIA Cg (C for Graphics) と同じ末路をたどるとは思いませんが、CUDA を選択するということはすなわち NVIDIA と命運をともにすることになる、ということくらいは肝に銘じておく必要があります。

*1:PTX (Parallel Thread Execution) はNVIDIA独自の中間命令体系で、CUDAでも使われています。

*2:読み書きをする場合は Image でなく Buffer を使わなければならず、つまり読み取り時に補間サンプリングをしたい場合は、ハードウェアに例え高速なサンプラーが用意されていても、自前でバイリニア フィルタリングなどのコードをわざわざ書かないといけなくなる。ピンポン処理で回避するにしても無駄にメモリを食う羽目になる。

*3:OpenCLGPGPU/HPC分野においては頼りないのですが、AlteraやXilinxFPGAなどに代表されるように、組み込み分野では徐々に活用が進んでいるようです。

F#でファイル・フォルダーをごみ箱へ移動

.NET Framework F# プログラミングTips Visual Studio

Visual Studioのバージョン2010以降をインストールすると、拡張子.fsxにVisual Studioが関連付けられ*1、さらにコンテキストメニューに「Run with F# interactive...」というコマンドが追加されます。.fsxはF#スクリプトと呼ばれるファイルなのですが、Visual Studioコードエディターを使ってF#コードを(インテリセンス付きで!)書けるようになり、さらにF# interatciveを使ってF#コードをスクリプトとして実行できる環境ができあがるわけです。

今回はF#を使って指定のファイル・フォルダーをまとめてごみ箱に移動する簡単なスクリプトを書いてみました。F# 3.xで動作確認しています。こういったOSのシェル機能を使ったタスクはWindows PowerShellの領域だったり、というかむしろC#で書いてしまったほうが返っててっとり早かったりするのですが、今後の将来性が見込まれるマルチパラダイム関数型言語F#の習得を今年から少しでも進めてみようということであえてF#を使っています。
ちなみに現状のfsxのインテリセンスはIronPython (Python Tools for Visual Studio) よりは使いやすいけれど、Visual C#には及ばない、といったレベルでしょうか。

// http://msdn.microsoft.com/ja-jp/library/vstudio/dd233175.aspx
#if INTERACTIVE
#r @"Microsoft.VisualBasic.dll"
#endif

//#light "off"

open System
open Microsoft.VisualBasic.FileIO

printfn "バックアップに不要なファイルおよびビルド中間生成物をごみ箱に移動します。"

let moveFileFolder fileFolderName moveFunc =
    try
        try
            // HACK: ファイルなのかフォルダーなのか判断して、呼び出す関数を分けるような分岐を実装してもよい。
            printfn "Now moving : <%s>" fileFolderName
            moveFunc(fileFolderName, UIOption.AllDialogs, RecycleOption.SendToRecycleBin)
        with
            | :? OperationCanceledException as ex -> printfn "移動がキャンセルされました。Message=\"%s\"" ex.Message
            | ex -> printfn "Message=\"%s\"" ex.Message
    finally
        ignore()

let moveFile fileName = moveFileFolder fileName FileSystem.DeleteFile
let moveFolder fileName = moveFileFolder fileName FileSystem.DeleteDirectory

let moveBinObjFolders baseDirName =
    moveFolder (baseDirName + @"\bin")
    moveFolder (baseDirName + @"\obj")

moveFile ".\\test.txt"
moveFile @"..\HogeBuild.v11.suo"
moveFolder @"..\.svn"
moveBinObjFolders @"..\FugaLib"

printf "Press any key to exit..."
Console.ReadKey(true) |> ignore

なお日本語表示など、Unicode対応する場合はfsxファイルをUTF-8で保存します。

はてなブログシンタックスハイライトは最近Swift言語に対応したらしいんですが、Microsoft発の関数型言語F#にも早く対応して欲しいです。とりあえず対応してくれるまではOCamlコードとしてハイライトすることにします……

*1:Windows 8.xではVisual Studioをインストールしてもfsxに関連付けられないことがあるようです。その場合はエクスプローラーなどから関連付けを手動設定してやる必要があります。