シェーダーで指定しているテクスチャを、全部は使わないときにValidatorを黙らせる方法です。
シェーダーで、例えば最大5枚のテクスチャを使うとしていても、モデルによっては3枚とかしか使わないときがありますよねぇ。
layout(binding = 0) uniform sampler2D texSampler[5];
ないものはしょうがないので、
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を作って、
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。
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)
物凄く気持ち悪い。
でも、データに応じてシェーダー側のテクスチャの数を変えるなんてできない。
一生懸命調べてみる。
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 を使う方法もあるとヒントが書いてある。行けそうだよね、語感的に。
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.
よしよし。
bindingの数のvk::DescriptorBindingFlagsの配列を作って、vk::DescriptorSetLayoutCreateInfoのpNext chainにくっつければよいのだな。
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が必要です。
そして芋づる式に次のDevice Extensionと、
このInstance Extensionが必要となります。
よし、設定完了。走らせてみる。
動くものの、まったく同じエラーが出続けるんですけど。
なんでだよー。
ひたすら検索…。
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にせんといかんようだ。
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で有効にして、
試行錯誤で、layout と poolには、Shaderで指定した数(今回は5)、descriptorSetには実際の数(今回は3)を設定すると、エラーがきれいになくなりました。
descritorSetには、imageViewやSamplerにVK_NULL_HANDLEを指定するのかと思いきやそれではうまくいきませんでした。設定の個数自体を減らすことでnullDescriptorを実現するようです。
とりあえずうまくいきましたが、これくらいほんのちょっとのことでもひたすら検索、検索、検索で疲れます…。