我已经阅读了Vulkan规范和Khronos的一些例子,但我找不到很多关于不可变采样器的信息。
我想做的是有一个描述符集,里面充满了不可变的采样器(类型为VK_descriptor_type_SAMPLER,而不是…COMBID_IMAGE_SAMPLER),当我想对纹理进行采样时,我会在着色器中访问这样的采样器:
layout (location = 0) out vec4 out_color;
layout (set = 0, binding = 0) uniform sampler immutableSampler;
layout (set = 3, binding = 0) uniform texture2D color;
void main()
{
vec4 textureColor = texture(sampler2D(color, immutableSampler), in_uv);
out_color = textureColor;
}
这个着色器的想法是:
- 在管道布局中,我在集合0的绑定点0处有一个不可变的采样器
- 我还有一个描述符集(3),其中在绑定点0处描述了一个图像,我将其绑定为命令缓冲区的一部分
- 我将不可变采样器应用于着色器中的图像
- 然后实际访问图像作为
sampler2D
然而,我在Khronos样本中看到的方法是:
- 描述管道布局中绑定点处的VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_sampler的不可变采样器(仅提供采样器)
- 创建一个图像并将其绑定到同一绑定点(无需采样器)
- 在着色器中以
sampler2D
的形式访问图像;您绑定的图像与该绑定处的不可变采样器配对
在Vulkan规范中,它没有说明哪种方式是"正确的",只是指出:
如果
descriptorType
指定了VK_DESCRIPTOR_TYPE_SAMPLER
或VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
类型描述符,那么pImmutableSamplers
可以用于初始化一组不可变采样器。
现在,如果我的着色器按照我预期的方式工作,我就不会问这个问题了。我遇到的问题是,在验证后,Vulkan层抱怨我的VkPipeline使用了一个从未绑定过的描述符集(集0,绑定采样器所在的0)!即使我在那里绑定了垃圾数据,我仍然必须(至少一次)用垃圾更新描述符集,以抑制另一个验证错误。然而,与此同时,Vulkan规范上写着:
不可变采样器被永久绑定到集合布局中;不允许稍后将采样器绑定到描述符集中的不可变采样器槽中
因此,甚至不允许将数据绑定到该绑定点!
我现在非常不确定我对不可变采样器的方法是否应该是有效的,因为它有点模糊。规范说我可以绑定没有图像的采样器,但当我这样做时会抱怨,我发现的几个例子是绑定组合图像采样器。我只是做错了吗?
如果我的方法被认为是有效的并且可以工作,我真的很希望这样,因为我正在努力为DirectX 12和Vulkan构建一个系统。DX12在其"根布局"(管道布局)中有一个位置,所有不可变采样器都被绑定,而没有与它们相关的图像。我想模仿这种行为来保持渲染器之间的奇偶性,但我不确定Vulkan是否真的可以做到这一点,而且它在规范中非常模糊。
您想要做的事情是完全可能和合法的。你遇到的问题是由于你对Vulkan规范没有说明的事情所做的假设。你正在创造不存在的暗示。
在这个例子中,我没有将任何描述符集绑定到集0,因为没有任何东西可以绑定,对吧?
错了。
这里的假设是不可变采样器描述符位于管道对象中。他们没有,Vulkan规范也从未声称他们有。
哦,是的,管道对象被赋予了描述符布局,其中包含不可变的采样器。因此,在构造对texture
函数的调用时,允许实现使用不可变的采样器数据。
但是描述符数据并不存在于管道中。它存在于描述符集中。
Vulkan规范在这一点上没有歧义。如果管道的布局包含描述符集,并且着色器在执行时使用该集,则必须在执行时绑定一个描述符集,该描述符集的布局与该集索引的管道布局兼容。
该实现真的会从一个不可变的采样器描述符中获取数据吗?这些不可变的描述符实际上会将数据存储在描述符内存中,还是将数据存储到管道中?谁在乎;Vulkan规范指出,所有使用的描述符都需要绑定。
这个规则没有例外。
但还有其他问题;设置0,绑定0是一个Sampler,但规范明确表示我不应该在那里绑定Sampler。
"绑定采样器"意味着调用vkUpdateDescriptorSets
。该函数更新不同集合中描述符中的数据。但它只更新你选择的数据。
此外,如果您尝试更新不可变采样器的采样器,它将忽略pImageInfo
中sampler
的值。这是由标准规定的(尽管是倾斜的):
sampler
是一个采样器句柄,如果正在更新的绑定不使用不可变采样器,则用于类型VK_DESCRIPTOR_TYPE_SAMPLER
和VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
的描述符更新
增加了重点。
由于存在该条件,因此如果描述符绑定使用不可变采样器,则sampler
将被忽略。毕竟,您可以将不可变采样器与图像/采样器组合使用。在这种情况下,pImageInfo
中的sampler
将被忽略。
这并不意味着这些描述符没有内容;您只是不允许更改它们。您可以在只包含不可变采样器的描述符集上调用vkUpdateDescriptorSets
;它就是不会有任何作用。
但是你仍然需要分配和使用这样一个集合。
如果我绑定了一个描述符集,但不更新它,我会在验证中遇到另一个错误,所以我需要将它与某种数据绑定以避免这种情况。
那么这就是验证层中的错误。你应该报告它。
Vulkan规范中没有规定在使用集合之前必须更新集合中的所有描述符。
要关闭有缺陷的验证层,请随意在验证构建时使用vkUpdateDescriptorSets
。不要麻烦传递有效的sampler
句柄,因为它实际上不会做任何事情。