Fix various descriptor handling bugs
- Use a custom descriptor structure for everything, so we have control
over alignment
- Fix descriptor pool size to include the proper number of headers
- Remove a level of indirection in buffer descriptor handling
- Correctly handle VK_WHOLE_SIZE for buffer bound ranges
- Handle robustness + dynamic offset interaction where the dynamic
offset places the bound range partially outside the buffer.
- Sanity checks from Ben's original 'seatbelts' patch
Bug: b/123244275
Test: dEQP-VK.binding_model.*
Change-Id: I18cc40805aac7256a7d57a21af003f21f630b2e6
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/30168
Presubmit-Ready: Chris Forbes <chrisforbes@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Tested-by: Chris Forbes <chrisforbes@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index 375bfeb..c7e489f 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -1334,20 +1334,23 @@
auto setLayout = routine->pipelineLayout->getDescriptorSetLayout(d.DescriptorSet);
int bindingOffset = static_cast<int>(setLayout->getBindingOffset(d.Binding, arrayIndex));
- Pointer<Byte> bufferInfo = set.base + bindingOffset; // VkDescriptorBufferInfo*
- Pointer<Byte> buffer = *Pointer<Pointer<Byte>>(bufferInfo + OFFSET(VkDescriptorBufferInfo, buffer)); // vk::Buffer*
- Pointer<Byte> data = *Pointer<Pointer<Byte>>(buffer + vk::Buffer::DataOffset); // void*
- Int offset = *Pointer<Int>(bufferInfo + OFFSET(VkDescriptorBufferInfo, offset));
- Int size = *Pointer<Int>(buffer + vk::Buffer::DataSize); // void*
+ Pointer<Byte> descriptor = set.base + bindingOffset; // BufferDescriptor*
+ Pointer<Byte> data = *Pointer<Pointer<Byte>>(descriptor + OFFSET(vk::BufferDescriptor, ptr)); // void*
+ Int size = *Pointer<Int>(descriptor + OFFSET(vk::BufferDescriptor, sizeInBytes));
if (setLayout->isBindingDynamic(d.Binding))
{
uint32_t dynamicBindingIndex =
routine->pipelineLayout->getDynamicOffsetBase(d.DescriptorSet) +
setLayout->getDynamicDescriptorOffset(d.Binding) +
arrayIndex;
- offset += routine->descriptorDynamicOffsets[dynamicBindingIndex];
+ Int offset = routine->descriptorDynamicOffsets[dynamicBindingIndex];
+ Int robustnessSize = *Pointer<Int>(descriptor + OFFSET(vk::BufferDescriptor, robustnessSize));
+ return SIMD::Pointer(data + offset, Min(size, robustnessSize - offset));
}
- return SIMD::Pointer(data + offset, size - offset);
+ else
+ {
+ return SIMD::Pointer(data, size);
+ }
}
default:
diff --git a/src/Vulkan/VkBuffer.cpp b/src/Vulkan/VkBuffer.cpp
index a83889b..287ff22 100644
--- a/src/Vulkan/VkBuffer.cpp
+++ b/src/Vulkan/VkBuffer.cpp
@@ -21,9 +21,6 @@
namespace vk
{
-const int Buffer::DataOffset = static_cast<int>(offsetof(Buffer, memory));
-const int Buffer::DataSize = static_cast<int>(offsetof(Buffer, size));
-
Buffer::Buffer(const VkBufferCreateInfo* pCreateInfo, void* mem) :
flags(pCreateInfo->flags), size(pCreateInfo->size), usage(pCreateInfo->usage),
sharingMode(pCreateInfo->sharingMode)
diff --git a/src/Vulkan/VkBuffer.hpp b/src/Vulkan/VkBuffer.hpp
index 2b86890..03cf95b 100644
--- a/src/Vulkan/VkBuffer.hpp
+++ b/src/Vulkan/VkBuffer.hpp
@@ -40,11 +40,6 @@
inline VkDeviceSize getSize() const { return size; }
uint8_t* end() const;
- // DataOffset is the offset in bytes from the Buffer to the pointer to the
- // buffer's data memory.
- static const int DataOffset;
- static const int DataSize;
-
private:
void* memory = nullptr;
VkBufferCreateFlags flags = 0;
diff --git a/src/Vulkan/VkDescriptorPool.cpp b/src/Vulkan/VkDescriptorPool.cpp
index da5bb39..8ac1558 100644
--- a/src/Vulkan/VkDescriptorPool.cpp
+++ b/src/Vulkan/VkDescriptorPool.cpp
@@ -36,13 +36,12 @@
size_t DescriptorPool::ComputeRequiredAllocationSize(const VkDescriptorPoolCreateInfo* pCreateInfo)
{
- size_t size = 0;
+ size_t size = pCreateInfo->maxSets * sizeof(DescriptorSetHeader);
for(uint32_t i = 0; i < pCreateInfo->poolSizeCount; i++)
{
size += pCreateInfo->pPoolSizes[i].descriptorCount *
- (sizeof(DescriptorSetHeader) +
- DescriptorSetLayout::GetDescriptorSize(pCreateInfo->pPoolSizes[i].type));
+ DescriptorSetLayout::GetDescriptorSize(pCreateInfo->pPoolSizes[i].type);
}
return size;
diff --git a/src/Vulkan/VkDescriptorSetLayout.cpp b/src/Vulkan/VkDescriptorSetLayout.cpp
index ac395b2..da86116 100644
--- a/src/Vulkan/VkDescriptorSetLayout.cpp
+++ b/src/Vulkan/VkDescriptorSetLayout.cpp
@@ -17,6 +17,7 @@
#include "VkDescriptorSet.hpp"
#include "VkSampler.hpp"
#include "VkImageView.hpp"
+#include "VkBuffer.hpp"
#include "VkBufferView.hpp"
#include "System/Types.hpp"
@@ -65,6 +66,7 @@
bindingOffsets[i] = offset;
offset += bindings[i].descriptorCount * GetDescriptorSize(bindings[i].descriptorType);
}
+ ASSERT_MSG(offset == getDescriptorSetDataSize(), "offset: %d, size: %d", int(offset), int(getDescriptorSetDataSize()));
}
void DescriptorSetLayout::destroy(const VkAllocationCallbacks* pAllocator)
@@ -89,44 +91,32 @@
size_t DescriptorSetLayout::GetDescriptorSize(VkDescriptorType type)
{
- size_t size = 0;
-
switch(type)
{
case VK_DESCRIPTOR_TYPE_SAMPLER:
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
- size = sizeof(SampledImageDescriptor);
- break;
+ case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
+ return sizeof(SampledImageDescriptor);
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
- size = sizeof(StorageImageDescriptor);
- break;
case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
- size = sizeof(VkDescriptorImageInfo);
- break;
- case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
- size = sizeof(VkBufferView);
- break;
+ return sizeof(StorageImageDescriptor);
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
- size = sizeof(VkDescriptorBufferInfo);
- break;
+ return sizeof(BufferDescriptor);
default:
UNIMPLEMENTED("Unsupported Descriptor Type");
return 0;
}
-
- // Aligning each descriptor to 16 bytes allows for more efficient vector accesses in the shaders.
- return sw::align<16>(size); // TODO(b/123244275): Eliminate by using a custom alignas(16) struct for each desctriptor.
}
size_t DescriptorSetLayout::getDescriptorSetAllocationSize() const
{
// vk::DescriptorSet has a layout member field.
- return sizeof(vk::DescriptorSetHeader) + getDescriptorSetDataSize();
+ return sw::align<alignof(DescriptorSet)>(OFFSET(DescriptorSet, data) + getDescriptorSetDataSize());
}
size_t DescriptorSetLayout::getDescriptorSetDataSize() const
@@ -258,8 +248,11 @@
{
this->sampler = sampler;
- texture.minLod = sw::clamp(sampler->minLod, 0.0f, (float)(sw::MAX_TEXTURE_LOD));
- texture.maxLod = sw::clamp(sampler->maxLod, 0.0f, (float)(sw::MAX_TEXTURE_LOD));
+ if (sampler)
+ {
+ texture.minLod = sw::clamp(sampler->minLod, 0.0f, (float) (sw::MAX_TEXTURE_LOD));
+ texture.maxLod = sw::clamp(sampler->maxLod, 0.0f, (float) (sw::MAX_TEXTURE_LOD));
+ }
}
void DescriptorSetLayout::WriteDescriptorSet(DescriptorSet *dstSet, VkDescriptorUpdateTemplateEntry const &entry, char const *src)
@@ -274,7 +267,27 @@
ASSERT(reinterpret_cast<intptr_t>(memToWrite) % 16 == 0); // Each descriptor must be 16-byte aligned.
- if(entry.descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
+ if (entry.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER)
+ {
+ SampledImageDescriptor *imageSampler = reinterpret_cast<SampledImageDescriptor*>(memToWrite);
+
+ for(uint32_t i = 0; i < entry.descriptorCount; i++)
+ {
+ auto update = reinterpret_cast<VkDescriptorImageInfo const *>(src + entry.offset + entry.stride * i);
+ // "All consecutive bindings updated via a single VkWriteDescriptorSet structure, except those with a
+ // descriptorCount of zero, must all either use immutable samplers or must all not use immutable samplers."
+ if (!binding.pImmutableSamplers)
+ {
+ imageSampler[i].updateSampler(vk::Cast(update->sampler));
+ }
+ }
+ }
+ else if (entry.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER)
+ {
+ UNIMPLEMENTED("VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER");
+ }
+ else if (entry.descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER ||
+ entry.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE)
{
SampledImageDescriptor *imageSampler = reinterpret_cast<SampledImageDescriptor*>(memToWrite);
@@ -486,16 +499,20 @@
descriptor[i].sizeInBytes = bufferView->getRangeInBytes();
}
}
- else
+ else if (entry.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ||
+ entry.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC ||
+ entry.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER ||
+ entry.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC)
{
- // If the dstBinding has fewer than descriptorCount array elements remaining
- // starting from dstArrayElement, then the remainder will be used to update
- // the subsequent binding - dstBinding+1 starting at array element zero. If
- // a binding has a descriptorCount of zero, it is skipped. This behavior
- // applies recursively, with the update affecting consecutive bindings as
- // needed to update all descriptorCount descriptors.
- for (auto i = 0u; i < entry.descriptorCount; i++)
- memcpy(memToWrite + typeSize * i, src + entry.offset + entry.stride * i, typeSize);
+ auto descriptor = reinterpret_cast<BufferDescriptor *>(memToWrite);
+ for (uint32_t i = 0; i < entry.descriptorCount; i++)
+ {
+ auto update = reinterpret_cast<VkDescriptorBufferInfo const *>(src + entry.offset + entry.stride * i);
+ auto buffer = Cast(update->buffer);
+ descriptor[i].ptr = buffer->getOffsetPointer(update->offset);
+ descriptor[i].sizeInBytes = (update->range == VK_WHOLE_SIZE) ? buffer->getSize() - update->offset : update->range;
+ descriptor[i].robustnessSize = buffer->getSize() - update->offset;
+ }
}
}
@@ -514,7 +531,7 @@
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
ptr = writeDescriptorSet.pTexelBufferView;
- e.stride = sizeof(*VkWriteDescriptorSet::pTexelBufferView);
+ e.stride = sizeof(VkBufferView);
break;
case VK_DESCRIPTOR_TYPE_SAMPLER:
diff --git a/src/Vulkan/VkDescriptorSetLayout.hpp b/src/Vulkan/VkDescriptorSetLayout.hpp
index 9ee4b3b..be92b4e 100644
--- a/src/Vulkan/VkDescriptorSetLayout.hpp
+++ b/src/Vulkan/VkDescriptorSetLayout.hpp
@@ -48,6 +48,13 @@
int sizeInBytes;
};
+struct alignas(16) BufferDescriptor
+{
+ void *ptr;
+ int sizeInBytes; // intended size of the bound region -- slides along with dynamic offsets
+ int robustnessSize; // total accessible size from static offset -- does not move with dynamic offset
+};
+
class DescriptorSetLayout : public Object<DescriptorSetLayout, VkDescriptorSetLayout>
{
public: