Summary
The Vulkan backend can bind a stale, misaligned dynamic uniform-buffer offset for an inline constant resource that the current pipeline promotes to push constants. The unused fallback UBO remains in the descriptor set layout, so its dynamic offset must still satisfy Vulkan's alignment requirements.
Reproduced against the implementation in upstream master at 18bfa7b7563a0ef5b5fe074d37c2e8304100e965 with a compute-dispatch regression:
VUID-vkCmdBindDescriptorSets-pDynamicOffsets-01971
vkCmdBindDescriptorSets(): pDynamicOffsets[1] is 68,
but must be a multiple of device limit minUniformBufferOffsetAlignment 64.
GPU output can still be correct because the shader reads push constants rather than the fallback UBO. GPU readback alone therefore does not detect this API validation error.
Reproduction
Use a Vulkan device/context with Khronos validation enabled:
- Map a dynamic uniform buffer to establish a uniform-aligned allocation anchor.
- Map a small dynamic vertex buffer with
MAP_WRITE / MAP_FLAG_DISCARD until its allocation offset is not a multiple of minUniformBufferOffsetAlignment. On the tested device, vertex allocations require only 4-byte alignment, while UBOs require 64-byte alignment.
- Release the vertex buffer and immediately create a resource signature containing an inline constant resource. Its fallback UBO reuses the released dynamic buffer ID.
- Create a pipeline that promotes this resource to push constants, set the inline constants, and bind an SRB from that signature.
- Dispatch. The fallback UBO has never been mapped, but descriptor binding uses the vertex buffer's old allocation offset and reports VUID 01971.
Keeping the aligned anchor alive and checking the mapped-address difference modulo the UBO alignment makes the precondition explicit even when other tests have already used the context's dynamic heap.
Expected: the promoted resource's unused fallback UBO supplies a valid placeholder offset, while resources actually read through UBO descriptors retain their real offsets.
Actual: the unused UBO inherits an unrelated allocation's offset through its recycled dynamic buffer ID.
Root cause
PipelineResourceSignatureVkImpl retains a fallback dynamic UBO descriptor and backing buffer for every inline constant resource.
CommitInlineConstants only pushes data for the resource selected by the current pipeline; it does not map that resource's fallback UBO.
ShaderResourceCacheVk::WriteDynamicBufferOffsets nevertheless queries the buffer's dynamic offset with allocation verification disabled.
DeviceContextVkImpl::GetDynamicBufferOffset indexes previously mapped allocations using a recyclable dynamic buffer ID. An unmapped replacement buffer can consequently observe the previous owner's allocation offset.
The selection is pipeline-specific: the same SRB can use B as push constants in one pipeline and B as an emulated UBO in another. The SRB can also come from a different but compatible signature instance. A fix must therefore identify the promoted resource in the currently bound SRB cache, rather than assuming the PSO's signature owns the bound backing buffer. Zeroing every inline-constant offset would corrupt resources using UBO emulation.
Environment and validation
- Windows build 10.0.26200.0, x64 Debug.
- Visual Studio 2022 / MSVC 19.44.35228.0; CMake 3.31.12.
- NVIDIA GeForce RTX 5060, driver 610.88.
- Vulkan SDK / Khronos validation layer 1.4.350.
- Reproduced with both the API test environment's default HLSL compiler selection and explicit DXC selection.
- With the pre-fix offset-writing logic restored, two targeted regression cases failed with VUID 01971 in every one of 10 independent process runs (20 failing case executions), with randomized case order. Explicit DXC also reproduced the same VUID.
- Separate processes matter for repeated negative runs: the validation layer may suppress a VUID after its duplicate-message limit is reached.
A fix and regression tests in InlineConstantsTest.cpp will follow in a PR.
Summary
The Vulkan backend can bind a stale, misaligned dynamic uniform-buffer offset for an inline constant resource that the current pipeline promotes to push constants. The unused fallback UBO remains in the descriptor set layout, so its dynamic offset must still satisfy Vulkan's alignment requirements.
Reproduced against the implementation in upstream
masterat18bfa7b7563a0ef5b5fe074d37c2e8304100e965with a compute-dispatch regression:GPU output can still be correct because the shader reads push constants rather than the fallback UBO. GPU readback alone therefore does not detect this API validation error.
Reproduction
Use a Vulkan device/context with Khronos validation enabled:
MAP_WRITE / MAP_FLAG_DISCARDuntil its allocation offset is not a multiple ofminUniformBufferOffsetAlignment. On the tested device, vertex allocations require only 4-byte alignment, while UBOs require 64-byte alignment.Keeping the aligned anchor alive and checking the mapped-address difference modulo the UBO alignment makes the precondition explicit even when other tests have already used the context's dynamic heap.
Expected: the promoted resource's unused fallback UBO supplies a valid placeholder offset, while resources actually read through UBO descriptors retain their real offsets.
Actual: the unused UBO inherits an unrelated allocation's offset through its recycled dynamic buffer ID.
Root cause
PipelineResourceSignatureVkImplretains a fallback dynamic UBO descriptor and backing buffer for every inline constant resource.CommitInlineConstantsonly pushes data for the resource selected by the current pipeline; it does not map that resource's fallback UBO.ShaderResourceCacheVk::WriteDynamicBufferOffsetsnevertheless queries the buffer's dynamic offset with allocation verification disabled.DeviceContextVkImpl::GetDynamicBufferOffsetindexes previously mapped allocations using a recyclable dynamic buffer ID. An unmapped replacement buffer can consequently observe the previous owner's allocation offset.The selection is pipeline-specific: the same SRB can use B as push constants in one pipeline and B as an emulated UBO in another. The SRB can also come from a different but compatible signature instance. A fix must therefore identify the promoted resource in the currently bound SRB cache, rather than assuming the PSO's signature owns the bound backing buffer. Zeroing every inline-constant offset would corrupt resources using UBO emulation.
Environment and validation
A fix and regression tests in
InlineConstantsTest.cppwill follow in a PR.