Vulkan: shader で指定した数のテクスチャを使いたくないとき

　シェーダーで指定しているテクスチャを、全部は使わないときにValidatorを黙らせる方法です。

　シェーダーで、例えば最大5枚のテクスチャを使うとしていても、モデルによっては3枚とかしか使わないときがありますよねぇ。

```glsl:fragment.frag
layout(binding = 0) uniform sampler2D texSampler[5];
```

　ないものはしょうがないので、

```c++:vulkan.cpp
std::vector<vk::DescriptorSetLayoutBinding> bindings{
  vk::DescriptorSetLayoutBinding()
    .setBinding(0)
    .setDescriptorCount(3) // ここが5であって欲しいのに
    .setDescriptorType(vk::DescriptorType::eCombinedImageSampler)
    .setPImmutableSamplers(nullptr)
    .setStageFlags(vk::ShaderStageFlagBits::eFragment)
};

auto& layoutInfo =
  vk::DescriptorSetLayoutCreateInfo()
    .setBindingCount(static_cast<uint32_t>(bindings.size()))
    .setPBindings(bindings.data());

descriptorSetLayout = デバイス.createDescriptorSetLayoutUnique(layoutInfo);
```

　少ない数でlayoutを作って、

```c++:vulkan.cpp
std::vector<vk::DescriptorSetLayout> layouts(スワップチェインの数, static_cast<const vk::DescriptorSetLayout>(descriptorSetLayout.get()));
auto allocInfo =
  vk::DescriptorSetAllocateInfo()
    .setDescriptorPool(descriptorPool.get())
    .setDescriptorSetCount(スワップチェインの数)
    .setPSetLayouts(layouts.data());

descriptorSets = デバイス.allocateDescriptorSetsUnique(allocInfo);
```

　DescriptorSetsを作って、コマンドバッファにbindDescriptorSetsしてDrawすると、ちゃんと表示はされるものの、Validation Layer が文句を言う。Warningじゃなくて、Error。

```text:Console
Validation Error: [ VUID-VkGraphicsPipelineCreateInfo-layout-00756 ] Object 0: handle = 0x2907ad94c78, type = VK_OBJECT_TYPE_DEVICE; | MessageID = 0x45717876 | Shader expects at least 5 descriptors for binding 0 but only 3 provided The Vulkan spec states: layout must be consistent with all shaders specified in pStages (https://vulkan.lunarg.com/doc/view/1.2.170.0/windows/1.2-extensions/vkspec.html#VUID-VkGraphicsPipelineCreateInfo-layout-00756)
```

　物凄く気持ち悪い。
　でも、データに応じてシェーダー側のテクスチャの数を変えるなんてできない。
　一生懸命調べてみる。

https://www.gamedev.net/forums/topic/703087-vulkan-sampler-array/5409445/

>Personally, I create dummy objects for these cases (e.g. a simple generic sampler, a 1x1 image, etc.) that I reuse everywhere necessary.
Another way is to use VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT_EXT, if supported, which allows bindings to not contain valid descriptors if they are not dynamically used. 

　ってことで、足りない分は1x1のダミーイメージで埋めてる、と書いてある。やっぱそういう手段しかないのかしら。

　と思ったら、VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT を使う方法もあるとヒントが書いてある。行けそうだよね、語感的に。

https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkDescriptorBindingFlagBits.html
>VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT indicates that descriptors in this binding that are not dynamically used need not contain valid descriptors at the time the descriptors are consumed. A descriptor is dynamically used if any shader invocation executes an instruction that performs any memory access using the descriptor.

　よしよし。

https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkDescriptorSetLayoutBindingFlagsCreateInfo.html

　bindingの数のvk::DescriptorBindingFlagsの配列を作って、vk::DescriptorSetLayoutCreateInfoのpNext chainにくっつければよいのだな。

```c++:vulkan.cpp
std::vector<vk::DescriptorBindingFlags> flags(bindings.size(), vk::DescriptorBindingFlagBits::ePartiallyBound);
auto bindingFlagsCreateInfo =
  vk::DescriptorSetLayoutBindingFlagsCreateInfo()
    .setBindingCount(static_cast<uint32_t>(flags.size()))
    .setPBindingFlags(flags.data());

auto layoutInfo = 
  vk::DescriptorSetLayoutCreateInfo()
    .setBindingCount(static_cast<uint32_t>(bindings.size()))
    .setPBindings(bindings.data())
    .setPNext(&bindingFlagsCreateInfo);

descriptorSetLayout = デバイス.createDescriptorSetLayoutUnique(layoutInfo);
```
　そして、これ使うためには、以下のDevice Extensionが必要です。

https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VK_EXT_descriptor_indexing.html

　そして芋づる式に次のDevice Extensionと、

https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VK_KHR_maintenance3.html

　このInstance Extensionが必要となります。

https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VK_KHR_get_physical_device_properties2.html

　よし、設定完了。走らせてみる。

　動くものの、**まったく同じエラー**が出続けるんですけど。

　なんでだよー。

　ひたすら検索…。

https://stackoverflow.com/questions/61690244/is-there-anyway-to-set-the-vkdescriptorimageinfo-to-null-or-have-some-way-of-ski

>You are not quite correct, a combined image sampler still requires a valid sampler for some reason, and not only that, a nullDescriptor functionality have to be enabled.

　加えて、nullDescriptorをenableにせんといかんようだ。

https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkWriteDescriptorSet.html

>If the nullDescriptor feature is enabled, the buffer, acceleration structure, imageView, or bufferView can be VK_NULL_HANDLE.

　確かにimageViewあたりをnullにできると書いてある。

　nullDescriptorをenableするには、以下のDevice ExtensionをnullDescriptorをTRUEで有効にして、

https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VK_EXT_robustness2.html

　試行錯誤で、layout と poolには、Shaderで指定した数(今回は5)、descriptorSetには実際の数(今回は3)を設定すると、エラーがきれいになくなりました。

　descritorSetには、imageViewやSamplerにVK_NULL_HANDLEを指定するのかと思いきやそれではうまくいきませんでした。設定の個数自体を減らすことでnullDescriptorを実現するようです。

　とりあえずうまくいきましたが、これくらいほんのちょっとのことでもひたすら検索、検索、検索で疲れます…。