Vulkan のコードにおいて、テクスチャを使用する際に、サンプラとテクスチャとをまとめている VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER を設定する箇所がありました。
よく見かけるサンプルでは、 OpenGL でテクスチャを使用するのと同じ感覚で、 サンプラとテクスチャを1体にして使っていたりします。
ここで、他の設定種別をみてみると VK_DESCRIPTOR_TYPE_SAMPLER, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE とあるので Image と Sampler は別に設定することもできるのではないか?と思いました。
ディスクリプタセットレイアウト
vkCreateDescriptorPool で Image, Sampler らを使えるように準備しておく必要があります。 VK_DESCRIPTOR_TYPE_SAMPLER, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE らを設定した DescriptorPoolSize を設定します。
DescriptorPool が準備できたら、ここから VkDescriptorSetLayoutBinding を作成し、 VkDescriptorSetLayout を得るようにします。
VkDescriptorSetLayoutBinding については以下のように設定してみました。以下の例では頂点シェーダーで定数バッファを使う都合でその設定が入っています。
descriptorLayoutBindingForVSUniform = { 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT, nullptr}; descriptorLayoutBindingForTexture = { 1, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr }; descriptorLayoutBindingForSampler = { 2, VK_DESCRIPTOR_TYPE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr }; VkDescriptorSetLayoutBinding bindings[] = { descriptorLayoutBindingForVSUniform, descriptorLayoutBindingForTexture, descriptorLayoutBindingForSampler, }; createInfo.pBindings = bindings;
この例では、バインディングポイント 1 にテクスチャが、バインディングポイント 2 にサンプラーが設定されるものとしています。いずれもピクセルシェーダーで使用するものとなっています。
ディスクリプタセット
レイアウトが出来たらそこからディスクリプタセットを作成します。そこでディスクリプタセットの中身を描画前には設定する必要があります。
このときも、サンプラとテクスチャと別々に設定することになるので、その例を以下に示します。
// VkDescriptorImageInfo textureInfo, samplerInfo; textureInfo = { VK_NULL_HANDLE, m_imageView, VK_IMAGE_LAYOUT_GENERAL }; samplerInfo = { m_sampler, VK_NULL_HANDLE, VK_IMAGE_LAYOUT_UNDEFINED }; // VkWriteDescriptorSet writeTexture, writeSampler; writeTexture.dstSet = m_set; writeTexture.dstBinding = 1; writeTexture.dstArrayElement = 0; writeTexture.descriptorCount = 1; writeTexture.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; writeTexture.pImageInfo = &textureInfo; writeSampler.dstSet= m_set; writeSampler.dstBinding = 2; writeSampler.dstArrayElement = 0; writeSampler.descriptorCount = 1; writeSampler.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; writeSampler.pImageInfo = &samplerInfo;
このようなディスクリプタセットを使用すれば Texture, Sampler を別々に設定が可能です。
シェーダーについて
以下のようにバインディングポイントに対して設定しているので、シェーダーの中でもこれを受け取るように少し修正する必要があります。
- テクスチャ
- サンプラ
#version 450 layout(location=0) in vec4 InCol; layout(location=1) in vec2 InUV; layout(location=0) out vec4 OutCol; layout(set=0, binding=1) uniform texture2D myTexture; layout(set=0, binding=2) uniform sampler Sampler; void main() { OutCol = texture(sampler2D(myTexture, Sampler), InUV) * InCol; }
まとめ
Vulkan では DirectX11 以降と同じように、テクスチャとサンプラを分けて設定することも出来るようになっていました。
多くのサンプルではこのように分けて設定せずに、 CombinedImageSampler を使用しているものとなっています。おそらくそのほうが設定数も少なく、実行効率もよいものだと思われます。
ただ、分けて設定することが必要な場面もあるかも、と思って今回の記事を作成してみました。