-
Notifications
You must be signed in to change notification settings - Fork 243
Description
Describe the bug
When switching between pipelines with different layouts, all descriptors and push constants after the incompatibility are left undefined. If the incompatibility is the push constants (either their size or stage flags), then the push constants and all descriptor sets will be undefined, but if the incompatibility's only descriptor layout
A use case affected by this would be an app with a descriptor that's bound overall, e.g. to hold something potentially useful to all shader sets like the simulation time, and a mixture of shaders that make the push constants available to just the vertex shader and that make them available to both the vertex and fragment shaders. When switching between these shaders, the push constants and descriptor sets must all be rebound, and currently, they won't be. Typically, only some would be rebound, as the modelview part of the push constants is likely to be different for different draw calls (although the projection matrix typically wouldn't be, so would be undefined), and so will most descriptor sets, but the overall available-to-all-shaders descriptor set won't be.
This can be worked around by ensuring all shaders in an app have a compatible pipeline layout (at least up to the last descriptor set that won't end up rebound for other reasons), but sometimes this is impractical as it means replacing all the shaders that the VSG itself provides, or avoiding using things like vsg::Text
, which would use a VSG-provided shader by default. It's also a hassle and probably not a good idea to force all VSG users to learn the pipeline compatibility rules and ensure all the pipelines in their app have compatible layouts.
I've not yet seen any symptoms from this - my hardware seems to be remembering the supposedly-disturbed push constants and descriptor sets - but it does cause validation errors that look like:
2190 API High Miscellaneous 941228658 Validation Error: [ VUID-vkCmdDrawIndexed-None-08600 ] Object 0: handle = Graphics Pipeline 6600, type = VK_OBJECT_TYPE_PIPELINE; Object 1: handle = Pipeline Layout 421, type = VK_OBJECT_TYPE_PIPELINE_LAYOUT; | MessageID = 0x381a0272 | vkCmdDrawIndexed(): The VkPipeline Graphics Pipeline 6600 (created with VkPipelineLayout Pipeline Layout 6596) statically uses descriptor set 0, but set 0 is not compatible with the pipeline layout bound with vkCmdBindDescriptorSets (VkPipelineLayout Pipeline Layout 421)
The set push constant ranges is different from the non-compatible pipeline layout push constant ranges
The Vulkan spec states: For each set n that is statically used by a bound shader, a descriptor set must have been bound to n at the same pipeline bind point, with a VkPipelineLayout that is compatible for set n, with the VkPipelineLayout used to create the current VkPipeline or the VkDescriptorSetLayout array used to create the current VkShaderEXT , as described in Pipeline Layout Compatibility (https://vulkan.lunarg.com/doc/view/1.3.296.0/windows/1.3-extensions/vkspec.html#VUID-vkCmdDrawIndexed-None-08600)
or
92 API High Miscellaneous 941228658 Validation Error: [ VUID-vkCmdDrawIndexed-None-08600 ] Object 0: handle = Command Buffer 592, type = VK_OBJECT_TYPE_COMMAND_BUFFER; Object 1: handle = Graphics Pipeline 568, type = VK_OBJECT_TYPE_PIPELINE; | MessageID = 0x381a0272 | vkCmdDrawIndexed(): VkPipeline Graphics Pipeline 568 uses set #1 but that set is not bound. (Need to use a command like vkCmdBindDescriptorSets to bind the set).
The Vulkan spec states: For each set n that is statically used by a bound shader, a descriptor set must have been bound to n at the same pipeline bind point, with a VkPipelineLayout that is compatible for set n, with the VkPipelineLayout used to create the current VkPipeline or the VkDescriptorSetLayout array used to create the current VkShaderEXT , as described in Pipeline Layout Compatibility (https://vulkan.lunarg.com/doc/view/1.3.296.0/windows/1.3-extensions/vkspec.html#VUID-vkCmdDrawIndexed-None-08600)
Possible solutions include:
- dirtying the matrix stacks and any state stacks that contain
BindDescriptorSet
every time the current pipeline layout changes. This will cause a lot of redundant descriptor set rebinding (although I'm already seeing that problem with the view descriptor sets, so that's something to look into later - I suppose that if this weren't happening, we'd have noticed the problem sooner). - Adding a check for pipeline layout compatibility, and dirtying everything when there's a change. This, too, will cause redundant rebinding, but at least avoids rebinding when the same layout is created twice.
- Adding a detailed check for partial layout compatibility, and dirtying only the necessary stacks. This would be optimal in terms of the Vulkan calls that were made, but would mean tracking more things. The actual comparison to work out the push constant compatibility and value of
$N$ for descriptor set compatibility would be as fast as checking compatibility at all using the currentcompare
implementation (although we'd have to do the comparisons in a different order), but the extra checking of the comparison result to decide whether to dirty the matrix stacks and which state stacks needed dirtying would be an extra cost.
All these possible fixes would be easier if the descriptor set bindings weren't mixed in with other state, as at the moment, we'd have to inspect every stack to see what was at its top to decide what to dirty, and that overhead could be entirely avoided if there were a direct way to access only the state stacks containing BindDescriptorSet
s. As I believe that's is one of the things you want to address next, it might be sensible to put off fixing this for a bit.