I want to pass a uniform buffer object containing two 4x4 matrices to the vertex shader. I have declared the struct in C++ as follows:
struct PerRenderUBO
{
glm::mat4 viewProjection;
glm::mat4 projection; //unused
};
And in GLSL:
layout(std140, set = 0, binding = 0) uniform UBO {
mat4 viewProjection;
mat4 projection; //unused
} perRenderUBO;
However, as soon as I declare more than one member in my UBO struct, some objects get drawn incorrectly due to a wrong viewProjection matrix. If i comment out the 'projection' data member in both the PerRenderUBO struct and the GLSL declaration, everything renders correctly (although I didn't even use the projection in the shader).
This leads me to believe that there must be something wrong with the data alignment. I've declared the layout with the std140 annotation. sizeof(PerRenderUBO) returns 128. I've tried to declare 'projection' as float and the problem persists.
Here are the most important parts of the descriptor set creation:
/** Create Layout **/
vk::DescriptorSetLayoutBinding perRenderUBOBinding;
perRenderUBOBinding.binding = 0;
perRenderUBOBinding.descriptorCount = 1;
perRenderUBOBinding.descriptorType = vk::DescriptorType::eUniformBuffer;
perRenderUBOBinding.stageFlags = vk::ShaderStageFlagBits::eVertex;
perRenderUBOBinding.pImmutableSamplers = nullptr;
std::vector<vk::DescriptorSetLayoutBinding> bindings{perRenderUBOBinding};
vk::DescriptorSetLayoutCreateInfo createInfo;
createInfo.bindingCount = bindings.size();
createInfo.pBindings = bindings.data();
this->perRenderUBOLayout = vkDevice->createDescriptorSetLayout(createInfo);
/** Create host-visible and host-coherent buffer **/
vk::BufferCreateInfo bufferCreateInfo;
bufferCreateInfo.size = sizeof(PerRenderUBO);
bufferCreateInfo.usage = vk::BufferUsageFlagBits::eUniformBuffer;
bufferCreateInfo.sharingMode = vk::SharingMode::eExclusive;
bufferCreateInfo.queueFamilyIndexCount = queueFamilyIndices.size();
bufferCreateInfo.pQueueFamilyIndices = queueFamilyIndices.data();
this->buffer = vkDevice->createBuffer(bufferCreateInfo);
vk::MemoryRequirements memoryRequirements = vkDevice->getBufferMemoryRequirements(this->buffer);
//allocates device memory as proposed in the specification (10.2 Device Memory)
this->bufferDeviceMemory = allocate(memoryRequirements, vk::MemoryPropertyFlags{vk::MemoryPropertyFlagBits::eHostVisible} | vk::MemoryPropertyFlags{vk::MemoryPropertyFlagBits::eHostCoherent});
vkDevice->bindBufferMemory(this->buffer, this->bufferDeviceMemory, 0);
this->descriptorPool = ...//create a descriptor pool for 1 uniform buffer
this->descriptorSet = ...//allocate descriptor set for above layout
vk::DescriptorBufferInfo bufferInfo;
bufferInfo.buffer = this->buffer;
bufferInfo.offset = 0;
bufferInfo.range = sizeof(PerRenderUBO);
vk::WriteDescriptorSet writeDescriptorSet;
writeDescriptorSet.dstSet = *this->descriptorSet;
writeDescriptorSet.dstBinding = 0;
writeDescriptorSet.dstArrayElement = 0;
writeDescriptorSet.descriptorType = vk::DescriptorType::eUniformBuffer;
writeDescriptorSet.descriptorCount = 1;
writeDescriptorSet.pBufferInfo = &bufferInfo;
writeDescriptorSet.pImageInfo = nullptr;
writeDescriptorSet.pTexelBufferView = nullptr;
vkDevice->updateDescriptorSets({writeDescriptorSet}, {});
Before executing the primary draw command buffer, I update the buffer for the PerRenderUBO as follows:
std::vector<PerRenderUBO> data; //contains 1 instance of PerRenderUBO
vk::DeviceSize offset = 0;
vk::DeviceSize size = data.size() * sizeof(PerRenderUBO);
void* memory = vkDevice->mapMemory(this->bufferDeviceMemory, offset, size);
std::memcpy(memory, data.data(), size);
this->deviceMemory->unmap();
I've checked buffer sizes and offsets several times and everything looks fine. Also, due to the fact that the same descriptor set is bound for every draw command and some objects render correctly, I believe that the data in the buffer itself must be correct. What am I missing?