堅牢性を使用する一般的なケースは以下の通りである。
-
悪意のあるメモリーアクセスを防ぐ必要がある(例:WebGPU)。
-
シェーダがアウトオブバウンズにならないことを保証できない。
-
他の場所で観察されたアウトオブバウンズ動作を模倣する。
Note
|
important
堅牢性を有効にすると、実行時パフォーマンスのコストが発生する場合があります。アプリケーションの作成者は、堅牢性を有効にするかどうかを慎重に検討する必要があります。 |
すべての Vulkan の実装は robustBufferAccess
機能をサポートする必要があります。仕様では、何が境界を越えたとみなされるか、またどのように処理されるべきかが説明されています。robustBufferAccess
については、実装にある程度の柔軟性が与えられています。たとえば、vec4(x,y,z,w)
へのアクセスで w
の値が境界を越えた場合、仕様では x
、y
、z
も境界を越えたとみなすかどうかを実装が決定できるようになっています。
robustBufferAccess
機能は、バッファのみを対象としており、イメージは対象外であるため、いくつかの制限があります。また、アクセスされているバッファのデータを変更する境界を越えた書き込みやアトミックを許可してしまいます。より強力な堅牢性を求めるアプリケーションのために、VK_EXT_robustness2 があります。
イメージが境界を越えた場合、コア Vulkan では、ストアやアトミックがアクセスされるメモリに影響を与えないことが保証されています。
以下は、robustBufferAccessの使用例である。(オンラインで試す)
#version 450
layout(set = 0, binding = 0) buffer SSBO {
// VkBufferは64バイトしかない。
// [0:63]からのインデックスは有効で、残りはOOBです。
uint data[128];
};
void main() {
// 実行時にはOOBになります。
// robustBufferAccessでは破棄されます。
data[96] = 0;
// robustBufferAccessではゼロを返す。
uint x = data[127];
}
VK_EXT_image_robustness の robustImageAccess 機能は、イメージビューのサイズに対する境界を越えたアクセスをチェックします。イメージの境界をへのアクセスがあった場合、(0, 0, 0, 0)
または (0, 0, 0, 1)
を返します。
robustImageAccess
機能は、無効な LOD へのアクセスに対して返される値については何の保証もありません。
D3D12 などの他の API から移植されるアプリケーションなどでは、robustBufferAccess
や robustImageAccess
が提供するものよりも厳しい保証が必要になる場合があります。VK_EXT_robustness2 拡張機能では、以下のセクションで説明する3つの新しい堅牢性機能を公開することで、保証を追加しています。一部の実装では、これらの追加保証はパフォーマンスを犠牲にしています。追加の堅牢性を必要としないアプリケーションでは、可能な限り robustBufferAccess
や robustImageAccess
を代わりに使用することをお勧めします。
robustBufferAccess2 機能は robustBufferAccess
のスーパーセットと見なすことができます。
この機能を有効にすると、境界を越えた書き込みやアトミックがバッファのメモリを変更することができなくなります。また、robustBufferAccess2
機能は、仕様書に記載されているように、境界を越えたアクセスしたときに、さまざまなタイプのバッファに対して返さなければならない値を強制的に設定します。
バッファが範囲チェックされる場所のアライメントは実装によって異なるため、VkPhysicalDeviceRobustness2PropertiesEXT から robustUniformBufferAccessSizeAlignment
および robustStorageBufferAccessSizeAlignment
をクエリすることが重要です。
robustImageAccess2 機能は robustImageAccess
のスーパーセットと見なすことができます。この機能は、アクセスしているイメージビューのサイズに対する境界を越えたかどうかのチェックをベースに、どのような値を返すことができるかについて、より厳しい要件を追加しています。
robustImageAccess2
では、R、RG、RGB フォーマットへの境界を越えたアクセスは (0, 0, 0, 1)
を返します。VK_FORMAT_R8G8B8A8_UNORM
のような RGBA フォーマットの場合は、(0, 0, 0, 0)
を返します。
また、robustImageAccess2
を有効にして、サポートされていないイメージ LOD にアクセスした場合は、境界を越えたとみなされます。
nullDescriptor 機能が有効になっていない場合、VkDescriptorSet
を更新するときには、たとえディスクリプタがシェーダで静的に使用されていなくても、対応するすべてのリソースは非ヌルでなければなりません。この機能により、ディスクリプタをヌルのリソースまたはビューと対応させることができます。ヌルディスクリプタからのロードはゼロ値を返し、ヌルディスクリプタへのストアとアトミックは破棄されます。
nullDescriptor
機能は、vkCmdBindVertexBuffers::pBuffers
がヌルの場合にも、頂点入力バインディングへのアクセスを可能にします。
堅牢性は実装によってはパフォーマンスコストとなることがあるため、VK_EXT_pipeline_robustness 拡張が追加され、開発者は必要な部分のみ堅牢性を要求できるようになりました。
VkPipeline`
の作成時に、1 つまたは複数の VkPipelineRobustnessCreateInfoEXT
構造体を渡すことで、パイプライン全体またはパイプラインステージ単位で、バッファ、イメージ、および頂点入力リソースへのアクセスの堅牢性動作を指定できます。
この拡張は、堅牢性機能が有効でない場合にデフォルトで提供される動作を実装に問い合わせる VkPhysicalDevicePipelineRobustnessPropertiesEXT
も提供します。
VK_EXT_descriptor_indexing
(Vulkan 1.2のコア)にあるバインド機能の後の更新を扱う場合は、実装が robustBufferAccess
とディスクリプタのバインド後の更新機能の両方をサポートするかどうかを示す robustBufferAccessUpdateAfterBind を確認することが重要です。