1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2021-06-05

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

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

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

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

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を作って、

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。

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)

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

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にくっつければよいのだな。

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が必要です。

 そして芋づる式に次の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を実現するようです。

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

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?