I got dynamic uniform buffers working last night.
In the code below, I initialize a dynamic offset, set the projection/view initially, then in a loop, set the dynamic offset to be i * the bufferSize, ubo's pvm matricies, then bind the descriptor set and draw.
This thing worked like a charm with a very random magic number I tried (21). But if I try 22 or higher, the thing just freezes and crashes. Just curious, is my approach to this incorrect? I must be missing something if I can't go over 21 loops. I have a GTX 3060 as well.
bufferSize for context is the size of the uniformBufferObject struct, which is just three mat4s (64 * 3 = 192).
uint32_t dynamicOffset = 0;
glm::mat4 proj = glm::perspective(fov, (float)screenWidth / (float)screenHeight, zNear, zFar);
proj[1][1] *= -1;
glm::mat4 view = glm::lookAt(glm::vec3(0.0f, 0.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
for (int i = 0; i < 21; i++)
{
dynamicOffset = bufferSize * i;
// Update and draw the cube
uniformBufferObject[i].proj = proj;
uniformBufferObject[i].view = view;
uniformBufferObject[i].model = glm::mat4(1.0f);
uniformBufferObject[i].model = glm::translate(uniformBufferObject[i].model, glm::vec3(0.0f, 0.0f, -3.0f));
uniformBufferObject[i].model = glm::rotate(uniformBufferObject[i].model, 0.0f, glm::vec3(1.0f, 1.0f, 1.0f));
uniformBufferObject[i].model = glm::scale(uniformBufferObject[i].model, glm::vec3(0.45f));
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipelineLayout, 0, 1, &descriptorSet, 1, &dynamicOffset);
vkCmdDraw(commandBuffer, static_cast<uint32_t>(vertices.size()), 1, 0, 0);
}
Trying to render hundreds of spheres using a dynamic UBO but when I try to access uniformBufferObject[where index = bufferSize (192) * 21 (4032)], the application crashes.
Uniform buffer creation code is here:
void createUniformBuffer()
{
createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, uniformBuffer, uniformBufferMemory);
vkMapMemory(device, uniformBufferMemory, 0, bufferSize, 0, &uniformBufferMapped);
uniformBufferObject = static_cast<UniformBufferObject*>(uniformBufferMapped);
}
Create buffer function:
void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory)
{
VkBufferCreateInfo bufferInfo{};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = size;
bufferInfo.usage = usage;
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
vkCreateBuffer(device, &bufferInfo, nullptr, &buffer);
VkMemoryRequirements memRequirements;
vkGetBufferMemoryRequirements(device, buffer, &memRequirements);
VkMemoryAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = memRequirements.size;
allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties);
vkAllocateMemory(device, &allocInfo, nullptr, &bufferMemory);
vkBindBufferMemory(device, buffer, bufferMemory, 0);
}
findMemoryType function:
uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) {
VkPhysicalDeviceMemoryProperties memProperties;
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties);
for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) {
return i;
}
}
}
ubo struct, descriptorsetlayout, descriptorpool, descriptorwrite:
struct UniformBufferObject
{
glm::mat4 model;
glm::mat4 view;
glm::mat4 proj;
};
VkDescriptorSetLayoutBinding uboLayoutBinding{};
uboLayoutBinding.binding = 0;
uboLayoutBinding.descriptorCount = 1;
uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
std::array<VkDescriptorPoolSize, 2> poolSizes{};
poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
poolSizes[0].descriptorCount = static_cast<uint32_t>(1);
poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
poolSizes[1].descriptorCount = static_cast<uint32_t>(1);
descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrites[0].dstSet = descriptorSet;
descriptorWrites[0].dstBinding = 0;
descriptorWrites[0].dstArrayElement = 0;
descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
descriptorWrites[0].descriptorCount = 1;
descriptorWrites[0].pBufferInfo = &bufferInfo;
Assuming this is the same
bufferSizeas your loop that populates the structure, this needs to becreateBuffer(bufferSize * <whatever number of draws you want active>, ...You're allocating a single 192 byte buffer, which happens to be at the start of a page, and so you you are able write over the end of the buffer until you hit the end of the page which then faults.