Lets say I have multiple meshes I want to render with different materials. I know I can use push constants
for this example, but this question is more to understand how vkDescriptorset is suppose to work.
struct Material {
vec4 color;
vkDescriptorset descriptorSet;
VkDescriptorBufferInfo descriptorBufferInfo;
};
I only call vkUpdateDescriptorSets
for _descriptorBufferInfo
after the buffer for the data has been created. Everything works fine.
I tested another solution. Instead of having one vkDescriptorset
per material, I have only one for all of them. And inside the rendepass
I call vkUpdateDescriptorSets
for the each of the material VkDescriptorBufferInfo
.
vkDescriptorset globalDescriptorSet;
struct Material {
vec4 color;
VkDescriptorBufferInfo descriptorBufferInfo;
};
beginCommandBuffer
beginRenderPass
for (auto &mesh : meshes) {
...
writeDescriptorSets.dstSet = globalDescriptorSet;
writeDescriptorSets.pBufferInfo = &mesh.material.descriptorBufferInfo;
....
vkUpdateDescriptorSets(device, 1, &writeDescriptorSets, 0, nullptr);
renderMesh(mesh);
}
endRenderPass
endCommandBuffer
But when I do this, it does not work. The validation layer says you have to call beginCommandBuffer
before calling any of the commands, vkCmdBindDescriptorSets
, vkCmdBindPipeline
etc for second mesh I render.
So what is the problem here, can I not share an vkDescriptorset between multiple VkDescriptorBufferInfo, or can I not update it inside a renderPass?
The update function is used to set which resources are used by a descriptor set. That’s what the pDescriptorWrites array contains. Each VkWriteDescriptorSet is a struct that contains a VkDescriptorSet object, a descriptor binding within that set to write to, and the resource (s) to attach to that descriptor binding.
Think of a single descriptor as a handle or pointer into a resource. That resource being a Buffer or a Image, and also holds other information, such as the size of the buffer, or the type of sampler if it’s for an image. A VkDescriptorSet is a pack of those pointers that are bound together.
A Renderpass will render into a Framebuffer. The framebuffer links to the images you will render to, and it’s used when starting a renderpass to set the target images for rendering. The general use of a renderpass when encoding commands is like this: When beginning a renderpass, you set the target framebuffer, and the clear color (if available).
Once a descriptor set is allocated, you need to write it to make it point into your buffers/textures. Once you bind a descriptor set and use it in a vkCmdDraw () function, you can no longer modify it unless you specify the VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT flag.
From the Vulkan specification:
The descriptor set contents bound by a call to vkCmdBindDescriptorSets may be consumed during host execution of the command, or during shader execution of the resulting draws, or any time in between. Thus, the contents must not be altered (overwritten by an update command, or freed) between when the command is recorded and when the command completes executing on the queue.
(emphasis added)
Presumably, your renderMesh
call binds the descriptor set to the command buffer. From that moment forward, you are not allowed to modify that descriptor set in any way until the commands in that command buffer have completed (or the CB was destroyed).
This is why dynamic uniform/storage buffers exist. They allow you to change the base offset of a uniform/storage buffer without being considered a modification of the actual descriptor set. So you can use the same descriptor set in multiple places with a different buffer offset.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With