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

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

Visual Studio 2015のコード整形機能のバグ

Visual Studioのコードエディターでは、C#コードを入力する際、ペースト時や閉じ中カッコ0x7Dあるいはセミコロン0x3Bを入力したタイミングなどで自動的にフォーマット(コード整形)が発動し、タブや空白などが自動調整されます。以前のバージョンのVisual Studioでは、C++コードでは明示的なフォーマット機能(ショートカット:Ctrl+K, Ctrl+F)のみをサポートしていましたが、Visual Studio 2015では、C++でもC#に近いオートフォーマットがデフォルトで発動するようになりました。
ただし、VS2015のソースコードエディターは、Update 3時点でもオートフォーマットのバグをいくつか抱えています。大半はVS2013時点では存在していなかったリグレッションです。VS2017は試していないので、不具合が修正されているかどうか、それとも依然として存在するかどうかは分かりません。なお、たとえMS Connectに不具合報告しても、VS2015では修正されず、どうせ新しいバージョンでしか修正されないことが分かり切っているので、報告するつもりはありません。また、せっかくユーザーが報告した不具合のチケットを、あろうことか勝手に削除することがあるようなので、MS Connectにはもう報告しないつもりです。傲岸不遜で恥知らずなMSにはもう協力したくありません。

フォーマット機能は、コーディング規約にそぐわないコードを整形するときは便利なのですが、意図しない形に勝手に自動整形されてしまうとイライラの原因になるだけです。また、明示的なフォーマットと、自動フォーマットとで整形結果が異なるときもあります。特にC#ラムダ式まわりで違いが具現化するのでイライラします。オートフォーマットが嫌いな人は、Visual Studioの設定で無効化しておくのが無難だと思います。

例1 (C++)

OpenGLDirect3Dで頂点バッファの初期データを定義するときによくある例です。

namespace
{
    struct MyVertex { float X, Y, Z; float Color[4]; };
    // 以下の行をカット&ペーストすると、Z の後ろのコンマと Color の間の空白がなくなる。
    const MyVertex Vertex1 = { 0.0f, +0.5f, 0.0f, { 1.0f, 0.0f, 0.0f, 1.0f } };
}

例2 (C++)

MFCが自動生成するコードとしてよく見られるものを、抽象化した例です。

class CMyDialog
{
public:
    CMyDialog() {} // 標準コンストラクター

// ダイアログ データ
    enum { IDD = 1 };
};

上記コードに対して明示的なフォーマットを実行したときに期待される動作は「// ダイアログ データの行が1レベルだけインデントされること」ですが、実際にフォーマットを実行しても何も起こりません。一方、カット&ペーストすると、// ダイアログ データ// 標準コンストラクターの開始桁位置と揃うように勝手にタブ文字および空白が挿入されてしまいます。

例3 (C++, C#)

void MyFunc()
{
    const int MyConst = 0; // 定数。
    // 処理。
    if (false) {}
}

上記コードに対して明示的なフォーマットを実行したときに期待される動作は「何も変更されないこと」ですが、C#の場合は、// 処理。// 定数。の開始桁位置と揃うように勝手にタブ文字および空白が挿入されてしまいます。また、C++においても、カット&ペーストするとC#同様に勝手な整形が実行されてしまいます。
行末尾のコメントを禁止するコーディング規約を採用している場合はこの問題に遭遇することはないと思いますが、勝手に桁位置を揃えられてしまうとイライラしますね。

例4 (C#)

class MyClass
{
    // ここで
    // #if false
    // #
    // と入力すると、既存の #region および #endregion のインデントが消失する。
    #region Fields
    int field;
    #endregion
}

通常、#regionおよび#endregionは対象となるコードブロックのインデントレベルに合わせてインデントされますが、このバグが発動するとインデントが消失します。

Windows Updateとの戦い(2018年春の陣)

Windows 10 1703にて、2018-04-18にいくつかのセキュリティアップデートを適用したら、勝手にWindowsの休止状態 (hibernation) が解除されて復帰する(PC電源が勝手に入る)問題が多発するようになりました。休止状態はメモリ内容をストレージに保存してPC電源を落とすので、スリープよりも復帰に時間がかかりますが、休止中に停電が発生しても復帰ができるというメリットがあるので、就寝前にいつも使っています。

以前、休止状態の解除が頻発して困っていたときに、キーボードやマウス、ネットワークアダプターが原因かもしれないと思って、デバイスマネージャーにて[このデバイスで、コンピューターのスタンバイ状態を解除できるようにする]のチェックをすべて外していたので、これらのデバイスが原因である可能性は最初から除外しました。

まず、コマンドプロンプトを管理者権限で起動*1し、powercfgコマンドで前回の復帰の理由を調べます。

Microsoft Windows [Version 10.0.15063]
(c) 2017 Microsoft Corporation. All rights reserved.

C:\WINDOWS\system32>powercfg -lastwake
スリープ状態の解除履歴カウント - 1
スリープ状態の解除履歴 [0]
  スリープ状態の解除元カウント - 1
  スリープ状態の解除元 [0]
    種類: スリープ解除タイマー
    所有者: [SERVICE] \Device\HarddiskVolume2\Windows\System32\svchost.exe (SystemEventsBroker)
    所有者によって示された理由:

イベントビューアーのシステムログで「Power-Troubleshooter」を検索すると、以下のレコードが2018-04-20 05:35:00に記録されていました。

システムは低電力状態から再開しました。

スリープ時間: 2018-04-19T16:44:22.759254000Z
スリープ解除時間: 2018-04-19T20:34:53.450160600Z

スリープ状態の解除元: タイマー - svchost.exe

「スリープ解除タイマー」とは何者でしょうか? 調べてみると電源オプションで無効化できるそうです。

[コントロール パネル] > [ハードウェアとサウンド] > [電源オプション]にて、現在選択されている電源プランの[プラン設定の変更] > [詳細な電源設定の変更]をクリック。
[電源オプション]ダイアログで、[スリープ] - [スリープ解除タイマーの許可]が[有効]になっていました。コイツを[無効]に変更して[OK]または[適用]ボタンをクリック。

これで一安心……と思いきや、やはり休止状態が勝手に解除されてしまいました。

もう一度powercfgとイベントビューアーで調べます。

Microsoft Windows [Version 10.0.15063]
(c) 2017 Microsoft Corporation. All rights reserved.

C:\WINDOWS\system32>powercfg -lastwake
スリープ状態の解除履歴カウント - 1
スリープ状態の解除履歴 [0]
  スリープ状態の解除元カウント - 0

イベントログには2018-04-21 04:38:25に記録があります。

システムは低電力状態から再開しました。

スリープ時間: 2018-04-20T17:57:22.602822200Z
スリープ解除時間: 2018-04-20T19:38:20.464928200Z

スリープ状態の解除元: 不明

はい原因不明でした。

UpdateOrchestratorタスクグループ

タスクスケジューラで確認したところ、UpdateOrchestratorのRebootタスクが指定日時をトリガーとして登録されていました。
UpdateOrchestratorはWindows Updateに関わるタスクグループらしく、スリープや休止状態の勝手な解除を発生させる諸悪の根源です。UpdateOrchestratorを組み込んだMSのエンジニアは、本当に頭のおかしいキチガイでしょう。Windows 8.1からWindows 10に更新した後、しょっちゅうコイツに悩まされていましたが、ここ最近は小康状態だったので油断していました。1703でWindows Updateによる再起動前のリマインダーや、更新の一時停止(最大35日間)をできる機能が追加されたので、再起動の回数自体も減っていました。
ひとまずRebootタスクの[条件]にて、[タスクを実行するためにスリープを解除する]のチェックを外しておきましたが、次回のWindows Update後にはまた勝手に復活していることでしょう。なお、1709ではこの設定がUIから変更できなくなっているそうです。ユーザーにさらなる不自由を強いるとは許せませんね*2

しかし、UpdateOrchestratorのRebootタスクを修正しても、やはり休止状態が勝手に解除されてしまいました。解除元は相変わらず不明です。

WindowsUpdateタスクグループ

WindowsUpdateのAutomatic App Updateタスク*3およびScheduled Startタスクを修正すると改善されるとの情報あり。前者は4時間ごとに無期限に繰り返すよう設定されていました。後者は指定日時をトリガーとして登録されていました。
しかし、[タスクを実行するためにスリープを解除する]はともにチェックが外れていました。
ほかに、AUSessionConnectというタスクも登録されており、これは[タスクを実行するためにスリープを解除する]にチェックが入っていました。
ひとまずAutomatic App UpdateタスクとAUSessionConnectタスクを無効化しておきました。Scheduled Startタスクは無効化しても、一定時間経つと勝手に有効化されるようです。なお、AUSessionConnectは[タスクを実行するためにスリープを解除する]のチェックを外して[OK]をクリックしても、なぜか以下のようなメッセージが表示されて、変更を反映できません。

---------------------------
タスク スケジューラ
---------------------------
タスク スケジューラ サービスが利用できません。タスク スケジューラはサービスへの再接続を試行します。
---------------------------
OK
---------------------------

しかし、Automatic App UpdateタスクとAUSessionConnectタスクを修正しても、やはり休止状態が勝手に解除されてしまいました。解除元は相変わらず不明です。

powercfgコマンドを使って、スリープ解除元のデバイスとタイマーを列挙してみます。

Microsoft Windows [Version 10.0.15063]
(c) 2017 Microsoft Corporation. All rights reserved.

C:\WINDOWS\system32>powercfg -devicequery wake_armed
NONE

C:\WINDOWS\system32>powercfg -waketimers
[SERVICE] \Device\HarddiskVolume2\Windows\System32\svchost.exe (SystemEventsBroker) によって設定されたタイマーは 04:33:06 (2018-04-24) で有効期限が切れます。
  理由:
C:\WINDOWS\system32>

バイスは案の定存在しませんでしたが、タイマーが存在するようです。

グラフィックスドライバーの勝手な更新

休止状態の解除問題とは直接の関係はありませんが、Windows Updateの履歴を見ると、2018-01-21に、NVIDIA GeForceドライバーが384.94から388.13に許可なく勝手に更新されていました。グラフィックスドライバーは特に相性問題を起こしやすく、たいていのパワーユーザーはドライバーの更新に慎重になるのですが、これが勝手に更新されるとなるとかなり困ります。以前はデバイスドライバーの更新に関してはオプショナル(任意)だったはずなのですが……
ひとまずグループポリシーでデバイスドライバーの更新を無効化する([Windows Updateからドライバーを除外する]を[有効]に構成する)ことで対処しました。

Windows 10更新アシスタントの無限復活

3月頃には「Windows 10更新アシスタント」が勝手にインストールされて、1709のダウンロードが始まる事案が発生しました。更新アシスタントはコントロールパネルで削除しても無限に復活する、まさにゾンビのように厄介なアプリです。グループポリシーでは無効化できず、以下のサイトを参考にしてようやく抑制できました。
www.atmarkit.co.jp

MSはどうしても1709の導入ベースを増やしたがっているようですが、だからといってユーザーの許可なく勝手にアプリをインストールするなんて破廉恥極まりないですね。年2回の大型アップデートスケジュールも、無理やりスケジュールを守ろうとして品質の確保ができなくなってしまっているのが目に見えています。機能追加のためのアップデートとスケジュールではなく、スケジュール遵守のための無理な機能開発になってしまっていて、目的と手段が完全に入れ替わってしまっていますね。お粗末としか言いようがないです。そもそも大型アップデートにより追加あるいは削除される機能のうち、大半は誰も望んでいない変更です。

Windows 10はユーザーの自由と時間と情報を奪っている

Windows 8.1までは良くも悪くもエンドユーザーがWindows Updateのタイミングと内容を完全にコントロールすることができていました。翻ってWindows 10では、ユーザーにほとんど自由がありません。せっかく設定を変更して抑制しても、Windows Updateを適用すると(MSにとって都合のいいように)設定が毎回リセットされてしまいます。ましてや、余計な機能てんこ盛りで品質の低い大型アップデートを勝手にインストールして、ユーザーのシステムを台無しにするなんて、犯罪にも等しいです。また、MSは個人情報収集のためには手段を選びません。なりふり構わずあの手この手を使ってきます。知識がないライトユーザーは簡単に騙されて大量に個人情報をぶっこ抜かれてしまうでしょう。こういう汚いことを続けていれば、どんどんパワーユーザーが離れていくということにMSは気付かないんでしょうか。本当に過去最悪のOSです。Windows 10の汚らしい挙動と、MSの下品なゴリ押しのせいで、Direct3D 12*4もUWPも開発に使う気がすっかり消え失せました。開発環境としてずっと慣れ親しんできたWindowsですが、もうウンザリです。もはやWindowsプラットフォーム固有の開発には積極的に投資したくありません。

今のところゲームやクリエイティブツールの関係上、どうしてもWindowsを使わざるを得ない状況ですが、今後Wineの完成度が上がればいずれWindowsを捨ててLinuxに移行したいですね。ソフト電池のような認証ツールもWine環境に対応してくれたらいいのですが。

*1:余談ですが、最初のカレントディレクトリは通常権限だと「%UserProfile%」で起動する一方、管理者権限だと「%SystemRoot%\System32」で起動しますね。

*2:MSが運営している各種フォーラムには、こういった明らかにユーザーに不利益を強いているMSの姿勢すら擁護する大量のマゾ信者がグルとして涌いていて、本当に気持ち悪いです。

*3:Windowsストアアプリを自動更新するためのタスクらしいです。ところでストアアプリなんていう不自由なもの、誰が好き好んで使うんですか?

*4:Direct3DとHLSLの進化が停滞すると競合のOpenGL/VulkanとGLSLも停滞することが予想されるので、Direct3Dの更新自体は続けて欲しいのですが、個人的には今後Direct3D/MetalよりもVulkanのほうが普及率が高くなり、API統一が進むのではないかと予想しています。

Vulkanグラフィックスの簡単なレンダリングサンプル

このあいだ書いた記事「VulkanシェーダーでSub-group命令を使う」では、グラフィックスをすっ飛ばしていきなりコンピュートシェーダーで拡張命令を試すという変態的なことをしましたが、GPGPUに興味のない人には退屈な内容だったかもしれません。

以前、Direct3D 12の簡単な入門記事「Direct3D 12 (DirectX 12) の簡単なサンプル」を書いたときには、コマンドリストの中で複数のPipeline State Object (PSO) を切り替えて、異なるシェーダーを適用してトライアングルをレンダリングするためのテストコードを実装しました。
今回はVulkanを使ってまったく同じことをします。レンダリング結果はD3D12バージョンとまったく同じになるようにしました。あらかじめ断っておきますが、「簡単なレンダリングサンプル」と言ったな。あれは嘘だ。

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

例のごとくMFCと公式のVulkan C++ラッパーを使って省力化していますが、それでも初期化コードが鬼のように長くなりました。Queue Family IndexやMemory Type Indexなど、ハードウェアの構成や仕様の差異にプログラマーサイドが柔軟に対応できる能力がある反面、抽象化層が極端に薄く、やはりAPIとしてはDirect3D 12を遥かにしのぐ難易度です。なかでも最難関だと感じたのは、やはりD3D12でのDescriptor Heapに相当するDescriptor Setまわりでした。頂点バッファとUniform Buffer Object (UBO) を使っているだけなのに、すでにこの有様です。分かってはいましたが、これにインデックスバッファやテクスチャ、Shader Storage Buffer Object (SSBO) といったおなじみの要素が加わってくるとなると、もうあっという間に複雑度が爆発して個人レベルでは手に負えなくなるのは目に見えています。

ちなみに、前回D3D12で使ったHLSLシェーダーを、中間言語であるSPIR-Vにコンパイルして、Vulkanでもそのまま使うことができました。一見地味ですが、HLSLシェーダーを一切変更することなく、Vulkanからも利用できるというのは驚異的ですね。これこそが中間言語の威力です。Cg言語のようなソフトウェアトランスレーターとは訳が違います。Direct3DではHLSLが登場する前から、ベンダー非依存のシェーダーアセンブラ中間言語として導入されていたことで、ハードウェアやドライバーの差異を気にすることなく開発できていましたが、OpenGLはなぜもっと早く同様のシェーダー中間言語を導入してこなかったのか、本当に惜しまれます*1

相互運用について

OpenGLDirect3D 11には、VulkanやDirect3D 12との相互運用を可能にするレイヤーや拡張が用意されています。従来のGL/D3Dアプリにおいて、Vulkan/D3D12に完全移行することはできないが、大量のドローコールのせいでボトルネックになっている箇所だけどうしても高速化したいという場合には、そういった相互運用を検討してもよいかもしれません。ただ、個人的な経験(手痛い失敗経験)から言わせてもらうと、やはり安定したアプリケーション開発のためには、あちこちに手を広げてむやみに複雑な解決策を持ち込もうとせず、できるかぎり単一のAPIのみに絞ったほうがいいと思います。理由として、複数の言語やAPIおよびフレームワークを混在させたハイブリッド開発は、コーディングがメチャクチャ疲れるうえに不具合を混入させやすいからです。また、相互運用が標準機能でなく拡張機能の場合は、ベンダーやユーザープログラマーによって十分テストされていないことから潜在的に不具合を抱える確率が高くなります。単に相互運用の能力があるということと、実際にそれを製品コードに使う、ということには大きな隔たりがあります。
CUDA/OpenCLにもGL/D3Dとの相互運用機能が存在しますが、GPGPUによるシミュレーション結果を可視化したいときは、(CUDA/OpenCLを使ったほうが効率的に記述できる場合を除いて)できるかぎりGLSL/HLSLのコンピュートシェーダーを使うべきでしょう。

*1:OpenGL 4.6でGL_ARB_gl_spirvつまりSPIR-Vのサポートが標準化されましたが、2018年4月時点でフル対応ドライバーをリリースしているのはNVIDIAだけです。本家OpenGL中間言語が早期に実現されていれば、派生規格であるOpenGL ESでも採用されていたはずで、アプリケーション開発者がベンダードライバーごとのシェーダーコンパイラーの挙動の違いなんぞに苦労することもなかったと思います。

Windows 10の右クリックメニューにコマンドプロンプトを復活させる

Windows 7エクスプローラーで、Shiftキーを押しながらコンテキストメニュー(右クリックメニュー)を表示すると、「コマンド ウィンドウをここで開く」という隠しコマンドが表示され、コマンドプロンプトのカレントディレクトリをそのディレクトリに設定した状態で起動することができます。通例、コマンドプロンプトを起動するとカレントディレクトリは%UserProfile%に設定されるので、特定のディレクトリ(フォルダー)でバッチ処理をしたくなったときにディレクトリの移動が煩雑なのですが、そういうときにこの隠しコマンドが威力を発揮します。
Windows 8.xおよびWindows 10 Anniversary Update (1607) までは同様の機能を備えていました。

しかし、Windows 10 Creators Update (1703) では、「PowerShell ウィンドウをここに開く」というメニューコマンドが追加され、逆に「コマンド ウィンドウをここで開く」は削除されてしまいました。
Windows PowerShellは確かにコマンドプロンプトと比べて遥かに強力ですが、オプション書式に互換性がなく、従来のコマンドに複雑なオプションを指定した場合はそのままでは実行できなくなっているため、依然としてコマンドプロンプトの需要は残っています。

コマンドプロンプトを起動するためのメニューコマンドを復活させるには、レジストリをいじる必要があります。

毎回手作業で実行するのは面倒ですので、今回は「コマンド ウィンドウをここで開く」コマンド復帰の手順を一発で実行するためのレジストリファイルの内容を記載しておきます。UTF-16LE形式のテキストファイルで.reg拡張子を付けて保存してください。ただしご利用は自己責任でどうぞ。

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Classes\Directory\Background\shell\cmd]
"HideBasedOnVelocityId"=dword:00000000
"ShowBasedOnVelocityId"=dword:00639bc8

なお、上記のエントリを追加しただけでは、エクスプローラー右ペインのリストビューのディレクトリアイテムに対してコンテキストメニューを表示したときにメニューコマンドが表示されませんが、それほど困ることはないと思います。カレントディレクトリに設定したいフォルダーを開いて、リストビューの余白内でメニューを表示するか、あるいはエクスプローラー左ペインのツリービューのディレクトリアイテムに対してメニューを表示することでコマンドが出現するので、必要十分ではないでしょうか。
また、上記エントリは現在のWindowsログオンユーザーのレジストリのみに追加され、ほかのユーザーには影響しません。

ネットワークディレクト

ネットワークディレクトリ(\\で始まるUNCパス)は、コマンドプロンプトのカレントディレクトリとして使えないという制約があります。そのため、ネットワーク上の共有フォルダーにて「コマンド ウィンドウをここで開く」を実行すると、まずWindowsによって自動的にネットワークドライブレターが割り当てられてから、そのドライブ上で改めてコマンドプロンプトが起動するという動作をします。

コマンドプロンプトの将来

隠しコマンドにPowerShellを追加するにしても、コマンドプロンプトを残して両方使えるようにしておけばいいのに、なぜわざわざ削除したんでしょうか?
MSは将来的にレガシーなコマンドプロンプトを抹殺する気なのかもしれませんが、肝心のPowerShellが完全な上位互換でないのに移行できるわけがありません。
そんなにPowerShell推しなんだったら、まずVisual StudioのコマンドベースユーティリティをPowerShellに置き換えてみせろよと言いたいです。PowerShellのバージョンがフラグメンテーションを起こしていることもあるせいか、いまだにVisual StudioのツールはPowerShellでなくコマンドプロンプトに依存している部分が多々あります。

余談

実は、Shiftコンテキストメニューには、ほかにも「パスとしてコピー」(Windows 7)あるいは「パスのコピー」(Windows 8.x/10)という便利な隠しコマンドが用意されています。このコマンドはディレクトリおよびファイル両方に対して使用でき、実行することでクリップボードにアイテムの絶対パス文字列をコピーします。
ディレクトリのパスは通例エクスプローラー上部のアドレスバーをクリックすることでも簡単に取得できますが、Windows Vistaまではファイルのパスを1アクションで取得する機能がなく不便でした。こういった細かいユーティリティは、OSの標準機能として提供されていることが一番重要です。標準機能であればどの端末でも必ず使えることが保証されているので、サードパーティ製のプラグイン/ツール/サービスをインストールできない環境を使うときや、エンドユーザーあるいは同僚に操作の指示を出したりマニュアルを作成したりするときに重宝します。OSの設定を変更することなく、最初から既定でどのユーザーでも使えるようになっていることも必須条件ですね。

VulkanシェーダーでSub-group命令を使う

NVIDIAはKepler (Compute Capability 3.0) 世代のハードウェアにおいて、Warp Shuffle命令を実装しました。WarpシャッフルはCUDAから組み込み関数 (intrinsic function) の形で利用できるSIMD命令の一種で、Warpと呼ばれるスレッドグループ内での並列データ交換を実現する機能です。NVIDIAGPUでは、ひとつのWarpは32個のハードウェアスレッドからなり、またWarp内の各スレッドはすべて同じ命令を実行します。つまり、ひとつのWarp内のスレッド群はすべて同期して並列動作する*1ため、Warpシャッフルを使うことで、データ列の総和を求めるリダクション演算などを、共有メモリを使うよりも高速に並列実行することができます。

Warpシャッフルに関しては、以前「CUDA Warpシャッフル命令のエミュレーション」という記事を書きましたが、CUDAの共有メモリに関する知識があれば、Warpシャッフルがいかに便利で簡潔な機能であるかを理解できると思います。

WarpシャッフルのようなSIMD命令は、汎用的な計算(GPGPU)だけでなく、グラフィックスのポストエフェクト処理などの高速化にも非常に有用なのですが、APIの停滞のせいであまり標準化が進んでいませんでした。NVIDIAWarpシャッフル命令をHLSL/GLSLから利用できるベンダー拡張を提供していますが、当然NVIDIA環境でしか使えないという制約があります。

Warpシャッフル相当の機能はまた、OpenCL 2.0の拡張機能cl_khr_subgroupsおよびOpenCL 2.1のコア機能として策定されていますが、2018年3月現在、OpenCLの実装はどのベンダーも停滞しており、OpenCLには期待できない状況です。一方、OpenGLにもARB拡張GL_ARB_shader_ballotとして存在しており、OpenCL同様にサブグループ (Sub-groupあるいはSubgroup) と呼ばれています。GL_ARB_shader_ballotはCUDAのWarpシャッフルのサブセットであるものの、GLSLを使えばOpenGLだけでなくVulkanからも基本的なサブグループの機能を利用できます。

Vulkanの学習を兼ねて、コンピュートシェーダーにてサブグループ命令を利用するサンプルコードを書いてみました。実行にはVulkan 1.0とVK_EXT_shader_subgroup_ballot拡張をサポートする環境が必要となります。NVIDIAハードウェアの場合、Kepler世代以降であれば拡張をサポートしているはずです。サンプルはWindows環境向けですが、ウィンドウシステムへのアクセスを必要としないオフスクリーンのコンピュート機能だけを使っているため、他の環境に移植するのは比較的簡単だと思います*2

以前書いた記事「Vulkan SDK付属のGLSLコンパイラー」では、Vulkan SDK付属のシェーダーコンパイラーがあまりに未完成なことを嘆いていましたが、その後着々と進化を続け、ほぼ使えるレベルに達したようです。
なお、今回はホストコードの記述量を減らすため、C++向けのラッパー (vulkan.hpp) を使っています。もともとこのラッパーはNVIDIAから寄贈されたものをベースに公式化されたらしいのですが、設計不足な点が散見されたり、一部の関数テンプレートのコンパイルが通らないというひどいバグが混入していたりと、まるでテストされていないことがありありとうかがえます。はっきり言ってプロダクションコードに使うには厳しい印象です。本気でVulkanの導入を検討する場合は、ぶっちゃけこのラッパーは使わないほうがいいでしょう。

なお、AMDAnvil*3という上位レベルのVulkanベースフレームワークを公開していますが、いつも仕事が中途半端なAMDが、この先ちゃんと継続的にメンテしてくれるのかどうか不安です。AMD APP SDKも、2015年8月にリリースされたv3.0で放置されたままという体たらくなので、正直期待できません。

Vulkan 1.1とSub-group命令

先日Vulkanのアップデートとしてv1.1が発表されましたが、Direct3D 12が先行していた、マルチGPUの活用やシェーダーモデル (SM) 6.0に対応する形で、重要な新機能が追加されているようです。サブグループ演算 (subgroup operation) に関しても、既存のOpenGL互換のARB拡張とは別のKHR拡張 (GL_KHR_shader_subgroup) およびホストAPIの正式仕様が策定され、CUDAと同等あるいはそれ以上の機能を獲得したようです。

なお、DirectXにおけるSM 6.0の目玉とも言えるのが、Sub-groupに相当するWave命令セットです。Waveは機能レベル12_0以上でないと使えないとか、そもそもDirect3D 12自体がWindows 10でないと使えないとかいう制約があり、個人的にはプラットフォーム制約の少ないVulkanのほうが有望だと思うのですが、いずれにせよこれでようやくCUDA以外でも高速なSIMD命令が標準的に利用できる土台が整い始めました。
とはいえ、HLSLもGLSLも、CUDA C++に比べるとプログラマビリティは遥かに劣ります。個人的にはC++のテンプレートが使えないのが一番痛いです。純粋なGPGPU用途であれば、結局CUDAの地位は安泰であり、ただ単にNVIDIAハードウェアの能力を最大限活用できるAPIというだけでなく、地球上でもっとも生産性の高いGPGPU開発基盤であることは疑いの余地がありません。VulkanはせっかくSPIR-Vという強力なシェーダー中間言語を持って生まれたのだから、今後はシェーダーコンパイラーのさらなる飛躍と、Metalシェーディング言語のような新しいC++ベースのフロントエンドのサポートに期待したいところです。

*1:GPUコアの1サイクルでWarp内のすべてのスレッドが同時に駆動するとは限りませんが、論理的には必ず同期するようになっています。

*2:OpenGLでは仮にコンピュートを利用するだけであっても、GLコンテキストの作成にウィンドウハンドルを必要とし、しかも実行デバイスを明示的に選択する機能すら標準で提供されていませんでしたが、Vulkanは遥かに洗練されており、OpenCLやDirectComputeと似たような使い方ができるようになっています。

*3:Anvilとは「金床」(かなとこ)という意味ですが、UbisoftのAnvil Engineとカブっているので、できれば別の名称にして欲しかったです。

C/C++の#warning

コンパイルエラーを意図的に発生させる#errorプリプロセッサディレクティブに関しては、お馴染みの#includeや#defineなどと同様に言語仕様として標準化されているようで、おそらくすべてのC/C++処理系でサポートされています。

#ifdef __cplusplus
#error This code does not support C++!!
#endif

問題は警告のほうです。C/C++でユーザー定義の警告を発生させる方法は標準化されていません。
gccには#warningディレクティブが存在するのですが、Visual C++には存在しません。Clangにも存在しないようです。
#warningの実装は過去に提案されていたりするのですが、実現には至っていないようです。

#pragma messageはたいていの処理系でサポートされているのですが、単にメッセージを出力するだけで、警告にはならず、強制力がありません。

#ifdef NDEBUG
#pragma message("This code is compiled when release mode.")
#endif

また、#pragma messageでファイル名と行番号を出力しようとすると、__FILE__マクロと__LINE__マクロおよびトークンの文字列化と結合を駆使せねばならず、移植性のあるコードを記述するのが大変になります。

C#

一方、C#では#warningがちゃんと標準化されています。当然#errorもあります。

やはり本来は処理系ごとにどうにかするべき問題ではなく、C#のように言語仕様として標準化してほしい機能です。


え、Javaですか? そんな貧弱な言語は知りませんね。窓から投げ捨てましょう。

Windowsの画面キャプチャ取得方法

Windowsにおいて、画面のキャプチャ(スクリーンショット)を取得する方法はいくつかあるのですが、下記の標準機能は遥か昔(たぶんWindows 95あたり)から搭載されています。いずれもWindowsユーザーであれば誰もが知っていないとおかしいレベルの、初歩中の初歩です。

PrintScreenキー デスクトップ全体を1つの画像として取得します。マルチディスプレイ(マルチモニター)環境の場合、合成された1つの画像となります。
Alt+PrintScreenキー アクティブなウィンドウのみのスクリーンショットを取得します。ただしMDI子ウィンドウのみのキャプチャには対応していません。アクティブでない別のウィンドウがかぶさっている場合、そのウィンドウも映り込みます。

PrintScreenキーで取得したデータは、不可視のクリップボード(システム全体で共有するグローバルメモリ領域)にビットマップ画像データとして保存されます。オンメモリなので、当然Windowsをシャットダウン/再起動すると消失する揮発性のデータです。クリップボードに保存されたビットマップ画像データをファイルとして保存する場合は、お好みのペイントツールにてキャンバスに貼り付けて、所望のフォーマットで保存します。Windowsに標準搭載されているMS Paint(ペイント)を使う場合は、あらかじめキャンバスサイズを小さくしておけば、ペースト時に画像サイズに合わせてキャンバスを自動拡張してくれます。MS Word/Excel/PowerPointなど、Office系のソフトも直接クリップボード経由でドキュメント内に画像を貼り付けることができますが、設定によってはファイル埋め込みの際に、勝手に画像の解像度を落としてしまうこともあるので注意が必要です。サードパーティ製のアプリケーションを自由にインストールできる自宅のプライベート端末では、起動と動作が軽快なIrfanViewを使ってクリップボードデータをファイル保存することが多いです。

ちなみにスクリーンショットを画像ファイルとして保存するときは、通例可逆圧縮PNGフォーマットを使います。非可逆圧縮JPEGは主に写真向けの圧縮フォーマットであり、画面スクリーンショットの保存には向いていません(色変化の激しい部分でモスキートノイズが目立ったり、PNGよりもファイルサイズが増加したりします)。

なお、スクリーンショット画像にはマウスなどポインティングデバイスのカーソルが含まれません。カーソル合成前の画像となります。特に困ることはないと思いますが、もしマニュアル作成用途などで画像にカーソルをどうしても含めたい場合は、スクリーンショットにカーソルのアイコンをオーバーレイ描画した合成画像を自動生成できるツールがあるので、そういったものを利用する方法もあります。

Windows Vista以降

Windows Vistaではキャプチャ用のSnipping Toolが搭載されました。デスクトップ全体・指定ウィンドウ以外に、選択領域のキャプチャもできます。しかし、個人的には上記のPrintScreenキーとペイントツールを使った方法よりもかえってめんどくさいので使っていません。また、PrintScreenキーを使った方法であれば、コンボボックスのドロップダウンリストを展開表示した状態や、特定のUI要素をマウスオーバーした状態でスクリーンショットを取得することができますが、Snipping Toolではそういったことができません。

Windows 8以降

Windows 8において、Windowsキー+PrintScreenキーを押すと、"%UserProfile%/Pictures/Screenshots"にデスクトップ全体のスクリーンショットPNG形式で自動保存されるようになりました。これはわりと有用な機能だと思いますが、Windows 7以前では使えないので、知っている人は少ないかもしれません。

ただし、Alt+PrintScreenのようにアクティブウィンドウのみをキャプチャして直接ファイル保存する機能がありません。この点がかなり不満です。

Windows 10

Windows 10ではゲーム録画 (Game DVR) の機能が追加され、Windows+Alt+PrintScreenでアプリのスクリーンショットを取得・保存できるようになりました。保存場所は"%UserProfile%/Videos/Captures"だそうです。「ゲーム録画」と銘打っていますが、ゲームアプリ以外でも使えます。しかし、利用にはXboxアプリへのMicrosoftアカウントを利用したサインイン(ネットワーク接続)が必要です。ローカルアカウント中心で使っているユーザーや、企業ユーザーの場合は使えない手段でしょう。Game DVRは当初既定で無効化されていたものの、Anniversary Update (1607) にて既定で有効化されたようです。以降は無効化する場合にもXboxアプリへのサインインが必要だそうで、もう意味不明ですね。Game DVRは個人情報が勝手にぶっこ抜かれる可能性があるので、自分は使っていません。

なお、Creators Update (1703) では余計な機能満載のPaint 3Dが標準インストールされるようになったのですが、さらにFall Creators Update (1709) では、従来の標準ペイントツールMS Paintが廃止・非標準になりました。完全に廃止されたわけではなく、Windowsストア(Microsoftストア)から明示的にインストールすれば使えるらしいのですが、ストアへのアクセスにはやはりサインイン(ネットワーク接続)が必要となります。電卓アプリや画像ビューアーの劣化と同じような道をたどるようです。ぶっちゃけ「Creators Update」とかいいつつ、誰も望んでいない、使えないゴミアプリを搭載しただけのアップデートです。そもそもCreatorだったらAdobe/Autodeskなどのデファクトスタンダードサードパーティ製ツールを使うのでPaint 3Dなんぞ要らないですね。

MS Paint自体はAdobe Photoshopなどと比べると大した機能を持っていませんが、これまでは標準インストールされていたことが最大の強みでした。特に仕事で、好きなペイントツールが使えない(インストールされていない/できない)端末において、取得したスクリーンショットをファイル保存するときだけはMS Paintをよく使っていました。また、機能が少ないぶんシンプルで、操作体系も昔からほとんど変わっていないため、初心者でも使いやすいというメリットがありました。しかし、今後は仕事で他の人にスクリーンショットの取得を指示する場合、まずはPaint 3DもしくはSnipping Toolに慣れてもらう必要がありそうです。

Steam

Valveが提供・運営するSteamプラットフォーム向けのゲームでは、共通してF12キーでスクリーンショット画像をファイル保存できます。ウィンドウモードでもクライアント領域のみが保存されるので、Steamの場合はこちらのほうが便利でしょう。昔はJPEGフォーマットのみでしたが、現在は劣化の無いPNGでの保存もサポートされています。

余談

XPやVista/7はLunaやAeroといったVisualテーマを適用すると、ウィンドウの四隅が丸くなります。キャプチャすると四隅の部分は白色となりますが、個人的にはこの丸まった四隅がダサくて大嫌いでした。
また、過去のWindowsではタイトルバーにグラデーションが使われていたり、Windows Aero (Aero Glass) の機能でウィンドウが透過していたりしたのですが、これによりPNGキャプチャ画像は無駄にファイルサイズが大きくなりがちでした。なめらかに色変化する複雑な画像はPNGだと圧縮しづらいからです。Windows 8.xではデザイン方針としてModern UIを採用することでタイトルバーがシンプルな単色になり、PNGキャプチャ画像のファイルサイズも削減されました。しかし、Windows 10 1709では、Fluent Design Systemという、これまたMSの自己満足的デザイン方針が採用され、AppleiOSで採用されているような半透明のすりガラス(アクリル)効果が多用されることになったため、再びPNGキャプチャ画像のファイルサイズが無駄に増えそうです。