Multiple storage buffers


#1

I’m having a difficult time understanding the buffer description code. I have one shader storage buffer working and now I want to add some more.

This code gets called first:

	std::array<shared_ptr<GPUBuffer>, 3> storagebuffer;
	
	//--------------------------------------------------------------------
	// Create instance ID shader storage buffer
	//--------------------------------------------------------------------

	VkDescriptorSetLayoutBinding uboLayoutBinding = {};
	uboLayoutBinding.binding = 0;
	uboLayoutBinding.descriptorCount = 1;
	uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
	uboLayoutBinding.pImmutableSamplers = nullptr;
	uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;

	VkDescriptorSetLayoutCreateInfo layoutInfo = {};
	layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
	layoutInfo.bindingCount = 1;
	layoutInfo.pBindings = &uboLayoutBinding;

	if (vkCreateDescriptorSetLayout(device->device, &layoutInfo, nullptr, &descriptorSetLayout) != VK_SUCCESS) {
		throw std::runtime_error("failed to create descriptor set layout!");
	}

And then this is called for each context I create:

	//--------------------------------------------------------------------------
	// Storage buffers
	//--------------------------------------------------------------------------

	VkDescriptorPool descriptorPool = {};

	// Create descriptor pool
	VkDescriptorPoolSize poolSize = {};
	poolSize.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
	poolSize.descriptorCount = static_cast<uint32_t>(swapChainImages.size());

	VkDescriptorPoolCreateInfo poolInfo = {};
	poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
	poolInfo.poolSizeCount = 1;
	poolInfo.pPoolSizes = &poolSize;
	poolInfo.maxSets = static_cast<uint32_t>(swapChainImages.size());;

	if (vkCreateDescriptorPool(environment->device->device, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) {
		throw std::runtime_error("failed to create descriptor pool!");
	}

	// Create descriptor set
	std::vector<VkDescriptorSetLayout> layouts(swapChainImages.size(), environment->descriptorSetLayout);
	VkDescriptorSetAllocateInfo allocInfo = {};
	allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
	allocInfo.descriptorPool = descriptorPool;
	allocInfo.descriptorSetCount = 1;// static_cast<uint32_t>(swapChainImages.size());
	allocInfo.pSetLayouts = layouts.data();
	//descriptorSets.resize(swapChainImages.size());
	if (vkAllocateDescriptorSets(environment->device->device, &allocInfo, &descriptorSet) != VK_SUCCESS) {
		throw std::runtime_error("failed to allocate descriptor sets!");
	}

	// Descriptor buffer info
	VkDescriptorBufferInfo bufferInfo[16] = {};
	for (int n = 0; n < device->storagebuffer.size(); ++n)
	{
		bufferInfo[n].buffer = device->storagebuffer[n]->buffer;
		bufferInfo[n].offset = 0;
		bufferInfo[n].range = device->storagebuffer[n]->GetSize();
	}

	// Write descriptor sets
		VkWriteDescriptorSet descriptorWrite = {};
		descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
		descriptorWrite.dstSet = descriptorSet;
		descriptorWrite.dstBinding = 0;
		descriptorWrite.dstArrayElement = 0;

		descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
		descriptorWrite.descriptorCount = 1;

		descriptorWrite.pBufferInfo = &bufferInfo[0];
		descriptorWrite.pImageInfo = nullptr; // Optional
		descriptorWrite.pTexelBufferView = nullptr; // Optional

		vkUpdateDescriptorSets(environment->device->device, 1, &descriptorWrite, 0, nullptr);

Every time I try modifying the code I get a lot of validation errors about the number of buffer descriptors not matching, or something like that. How can I modify this code to accept three buffers?


#2

It’s kind of hard to answer your question when you don’t show us the code that’s actually causing your problem (and your code’s internal confusion where uboLayoutBinding is actually a storage buffer isn’t helping). It would also be useful to see what the actual validation errors are, as well as the shader code that’s being linked against this descriptor set layout.

How do you want it to “accept three buffers?” I can think of two completely different ways of doing that (three if you want them in different descriptor sets). Seeing the shader code you want to consume these descriptor sets would help narrow things down.


#3

I want one shader to have access to three shader storage buffers:

layout(set = 0, binding = 0) readonly buffer Block0 { uvec4 instanceID[]; };
layout(set = 0, binding = 1) readonly buffer Block1 { float data[]; };
layout(set = 0, binding = 2) readonly buffer Block2 { float moredata[]; };

The whole uniform / storage buffers system is incredibly complicated in Vulkan. I only got this far by copying and pasting a lot of code.


#4

This appears to work:
//--------------------------------------------------------------------
// Create instance ID shader storage buffer
//--------------------------------------------------------------------

	VkDescriptorSetLayoutBinding uboLayoutBinding[3];
	for (int n = 0; n < 3; ++n)
	{
		uboLayoutBinding[n] = {};
		uboLayoutBinding[n].binding = n;
		uboLayoutBinding[n].descriptorCount = 1;
		uboLayoutBinding[n].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
		uboLayoutBinding[n].pImmutableSamplers = nullptr;
		uboLayoutBinding[n].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
	}

	VkDescriptorSetLayoutCreateInfo layoutInfo = {};
	layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
	layoutInfo.bindingCount = 3;
	layoutInfo.pBindings = &uboLayoutBinding[0];

	if (vkCreateDescriptorSetLayout(device->device, &layoutInfo, nullptr, &descriptorSetLayout) != VK_SUCCESS) {
		throw std::runtime_error("failed to create descriptor set layout!");
	}

And then…

descriptorWrite.descriptorCount = 3;