Vulkan (vs.)? MultiDrawIndirect

[QUOTE=Christoph;40490]It appears that even the latest glslangvalidator causes the invocation of “gl_MaxCombinedTextureImageUnits (using array)” when binding the array with texture2D [78+].

It there anything else to do but to run the validator with “-V”?[/quote]

You can file a bug report with the validator project.

I did that, it was acknowledged as bug.
I shall see if arrayed textures work, tomorrow.

Thank you again.

Hi yet again,

I am still unsure how to actually bind an array of textures as I have not yet found an example:

Do I push the individual texDescriptors to the write descriptor, all of them using the same bindng point?

for (size_t i = 0; i < materials.size(); i++)
{
VkDescriptorImageInfo texDescriptor =

vkTools::initializers::descriptorImageInfo(
materials[i].diffuse.sampler,
materials[i].diffuse.view,
VK_IMAGE_LAYOUT_GENERAL);

// Binding 0: Diffuse texture
writeDescriptorSets.push_back(vkTools::initializer s::writeDescriptorSet(
descriptorSetMaterial,
VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
0,
&texDescriptor));

}

Best Regards

An array of opaque types (like textures) in Vulkan takes up a single binding point. This is why VkDescriptorSetLayoutBinding has a descriptorCount parameter. Similarly, VkWriteDescriptorSet has the ability to update part of an array, through the dstArrayElement and descriptorCount fields.

It’s not clear what it is you’re having a problem with.

VK_IMAGE_LAYOUT_GENERAL Unless you’re trying to simultaneously read from and write to that image (or some similar simultaneous operation), don’t use the general layout. Please use proper layout transitions.

Hi, I seem to have hit a problem and I am not convinced anymore, that I am the problem.

I have modified the scene rendering example to put the Material struct (3x vec4) into a storage buffer.

I want to render using 2 descriptor sets: Scene(Matrices: ubo in vs, set0 binding0), Materials(array of Material Structs: buffer in fs, set1 binding0).

However , when outputting the first vec4 of the struct as color I get randombly blinking tiny quads, that change hue depending on viewing direction. Somehow the ubo gets accessed, obviously.

Now when I bind the shader storage buffer to the scene set0, binding 1, all is fine.

I still assume that I am lacking some critical knowledge but the setup of multiple descriptor Sets seems straight forward.

I can provide the source if required, its not to different from the original samples

Thank you for pointing that out. I copied the showmap code, which obviously does not fit best.

Now, I am close but I have problems with separate sampler and texture array

I would appreciate some help with the setup:

Best Regards

The Layout and the descriptorset update:


//descriptorSetLayouts.Material
		{ 	
			// Descriptor set and pipeline layouts
			std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings;
			VkDescriptorSetLayoutCreateInfo descriptorLayout;

			// Set 1: Material data
			setLayoutBindings.push_back(vkTools::initializers::descriptorSetLayoutBinding(
				VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,//VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
				VK_SHADER_STAGE_FRAGMENT_BIT,
				0));

			//// Set 1: Samper
			setLayoutBindings.push_back(vkTools::initializers::descriptorSetLayoutBinding(
				VK_DESCRIPTOR_TYPE_SAMPLER,
				VK_SHADER_STAGE_FRAGMENT_BIT,
				1));

			for (size_t i = 0; i < materials.size(); i++)
			{
				// Set 1: Textures
				setLayoutBindings.push_back(vkTools::initializers::descriptorSetLayoutBinding(
					VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,//VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
					VK_SHADER_STAGE_FRAGMENT_BIT,
					2));
			}

		descriptorLayout = vkTools::initializers::descriptorSetLayoutCreateInfo(
			setLayoutBindings.data(),
			static_cast<uint32_t>(setLayoutBindings.size()));

		VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayouts.Material));
	}


	// Material Descriptor set
		{
			VkDescriptorSetAllocateInfo allocInfo =
				vkTools::initializers::descriptorSetAllocateInfo(
					descriptorPool,
					&descriptorSetLayouts.Material,
					1);

			VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSetMaterial));

			std::vector<VkWriteDescriptorSet> writeDescriptorSets;
			
			writeDescriptorSets.push_back(vkTools::initializers::writeDescriptorSet(
				descriptorSetMaterial,
				VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,//VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
				0,
				&uniformBuffer.material.descriptor));

				VkDescriptorImageInfo texDescriptor =
					vkTools::initializers::descriptorImageInfo(
						materials[0].diffuse.sampler,
						VK_NULL_HANDLE,
						VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);

				 
			writeDescriptorSets.push_back(vkTools::initializers::writeDescriptorSet(
				descriptorSetMaterial,
				VK_DESCRIPTOR_TYPE_SAMPLER,
				1,
				&texDescriptor));

			for (size_t i = 0; i < materials.size(); i++)
			{
				VkDescriptorImageInfo texDescriptor =

					vkTools::initializers::descriptorImageInfo(
						VK_NULL_HANDLE,
						materials[i].diffuse.view,
						VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);

				 
				 writeDescriptorSets.push_back(vkTools::initializers::writeDescriptorSet(
				 	descriptorSetMaterial,
				 	VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,//VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,//
				 	2,
				 	&texDescriptor));

			}



			vkUpdateDescriptorSets(device, writeDescriptorSets.size(), writeDescriptorSets.data(), 0, NULL);
		}

The above code is nonsense, in so far that no sampler is provided:

By adding an actual sampler things should make sense (but not yet work)


// Create our separate sampler 

				VkSamplerCreateInfo samplerCreateInfo = {};
				samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
				samplerCreateInfo.magFilter = VK_FILTER_NEAREST;
				samplerCreateInfo.minFilter = VK_FILTER_NEAREST;
				samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
				samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
				samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
				samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
				samplerCreateInfo.mipLodBias = 0.0;
				samplerCreateInfo.anisotropyEnable = VK_FALSE,
				samplerCreateInfo.maxAnisotropy = 0;
				samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER;
				samplerCreateInfo.minLod = 0.0;
				samplerCreateInfo.maxLod = 0.0;
				samplerCreateInfo.compareEnable = VK_FALSE;
				samplerCreateInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;

				VkDescriptorImageInfo samplerInfo = {};
				/* create sampler */
				vkCreateSampler(device, &samplerCreateInfo, NULL, &samplerInfo.sampler);

				writeDescriptorSets.resize(2);
			
				writeDescriptorSets[1] = {};
				writeDescriptorSets[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
				writeDescriptorSets[1].pNext = NULL;
				writeDescriptorSets[1].dstSet = descriptorSetMaterial;
				writeDescriptorSets[1].descriptorCount = 1;
				writeDescriptorSets[1].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
				writeDescriptorSets[1].pImageInfo = &samplerInfo;
				writeDescriptorSets[1].dstArrayElement = 0;
				writeDescriptorSets[1].dstBinding = 1;




Blast it! The problem appears to be the texture array.

Using a single Texture with separate Sampler works fine.
However, vulkan appears to ignore some stuff.

I do not have to provide a sampler on the host side. I can sample the texture regardless. Is that intentional?

Still, did I miss anything in the code above?

The problem with posting code that uses wrappers for the Vulkan API is that we cannot tell what’s going on. For example, what does vkTools::initializers::descriptorSetLayoutBinding do? Does that create a VkDescriptorSetLayoutBinding object? If so, why does it only have 3 parameters instead of the 5 that the actual object does? What does it fill in for the missing parameters?

We can’t know what your non-Vulkan wrapper API is doing. So when I see you looping over materials.size() and pushing back calls to descriptorSetLayoutBinding, I don’t know if that code is just wrong or if your wrapper transforms it into something that makes sense.

So I’m going to go against my standing policy to not give people code and just tell you the answer. If your intent with your layout is to have 3 bindings, one for a UBO, one for a sampler, and one array of textures, then this is what your descriptor set layout should look like:


const VkDescriptorSetLayoutBinding bindings[] =
{
	{0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr},
	{1, VK_DESCRIPTOR_TYPE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr},
	{2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, std::uint32_t(materials.size()), VK_SHADER_STAGE_FRAGMENT_BIT, nullptr},
};

const VkDescriptorSetLayoutCreateInfo desc =
{
	VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
	nullptr,
	0,
	ARRAY_COUNT(bindings),
	bindings,
};

Thank you for this excellent argument against wrapper functions.
I have updated my code sample and learned a lot.

Here is the layout binding, effectively the same as yours (the descriptocount in the wrapper was always 1)



		//descriptorSetLayouts.Material
		{
			// Descriptor set and pipeline layouts
			std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings;

			// Set 1: Material data
			setLayoutBindings.push_back(VkDescriptorSetLayoutBinding{
				0,												//binding;
				VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,				//descriptorType;
				1,												//descriptorCount;
				VK_SHADER_STAGE_FRAGMENT_BIT,					//stageFlags;
				0 												//pImmutableSamplers;
			});

		 	// Set 1: Sampler			 
		 	setLayoutBindings.push_back(VkDescriptorSetLayoutBinding{
		 		1,												//binding;			
		 		VK_DESCRIPTOR_TYPE_SAMPLER,						//descriptorType;
		 		1,												//descriptorCount;
		 		VK_SHADER_STAGE_FRAGMENT_BIT,					//stageFlags;
		 		0 												//pImmutableSamplers;
		 	});
		 	
		 	// Set 1: Texture	
		 	setLayoutBindings.push_back(VkDescriptorSetLayoutBinding{
		 		2,												//binding;
		 		VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,				//descriptorType;
		 		static_cast<uint32_t>(materials.size()),		//descriptorCount;
		 		VK_SHADER_STAGE_FRAGMENT_BIT,					//stageFlags;
		 		0												//pImmutableSamplers;
		 	});
			
			const VkDescriptorSetLayoutCreateInfo descriptorLayout =
			{
				VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,	//sType;
				nullptr,												//pNext;
				0,														//flags;
				static_cast<uint32_t>(setLayoutBindings.size()),		//bindingCount;
				setLayoutBindings.data(),								//pBindings;
			};

			VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayouts.Material));
		}

Which leaves the binding of descriptors to the set.
Something is still missing, since the textures are not sampled at all



// Material Descriptor set
		{
		

			VkDescriptorSetAllocateInfo descriptorSetAllocateInfo = {
				VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,	//sType;
				NULL,											//pNext;
				descriptorPool,									//descriptorPool;
				1,												//descriptorSetCount;
				&descriptorSetLayouts.Material					//pSetLayouts;
			};
			
			VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocateInfo, &descriptorSetMaterial));

			std::vector<VkWriteDescriptorSet> writeDescriptorSets;
			  
			 
		 	writeDescriptorSets.push_back(VkWriteDescriptorSet{
		 		VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, // sType;
		 		NULL,									// pNext;				
		 		descriptorSetMaterial,					// dstSet;
		 		0,										// dstBinding;
		 		0, 										// dstArrayElement;
		 		1,										// descriptorCount;
		 		VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,		// descriptorType;   //VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
		 		0,										// pImageInfo;
		 		&uniformBuffer.material.descriptor, 	// pBufferInfo;
		 		0										// pTexelBufferView;
		 	});
			
			VkDescriptorImageInfo imageInfo{
					materials[0].diffuse.sampler,
					VK_NULL_HANDLE,
					VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL 
			};
				
		 	writeDescriptorSets.push_back(VkWriteDescriptorSet{
		 	  	VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, // sType;
		 	  	NULL,									// pNext;				
		 	  	descriptorSetMaterial,					// dstSet;
		 	  	1,										// dstBinding;
		 	  	0, 										// dstArrayElement;
		 	  	1,										// descriptorCount;
		 	  	VK_DESCRIPTOR_TYPE_SAMPLER,				// descriptorType;
		 	  	&imageInfo,								// pImageInfo;
		 	  	0, 										// pBufferInfo;
		 	  	0										// pTexelBufferView;
		 	  	});

			 		 
		 	// concat textures of all materials
		 	for (int i = 0; i < materials.size(); i++)
		 	{ 
		 		VkDescriptorImageInfo imageInfo{
		 			VK_NULL_HANDLE,							 //sampler;
		 			materials[i].diffuse.view,				 //imageView;
		 			VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL //imageLayout;
		 		};
		 
		 		 writeDescriptorSets.push_back( VkWriteDescriptorSet{
		 			 VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,	// sType;
		 			 NULL,										// pNext;				
		 			 descriptorSetMaterial,						// dstSet;
		 			 2,											// dstBinding;
		 			 static_cast<uint32_t>(i), 					// dstArrayElement;
		 			 1,											// descriptorCount;
		 			 VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,			// descriptorType; //VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,//VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE
		 			 &imageInfo,								// pImageInfo;
		 			 0, 										// pBufferInfo;
		 			 0											// pTexelBufferView;
		 			});
		 
		 	}

			vkUpdateDescriptorSets(device, writeDescriptorSets.size(), writeDescriptorSets.data(), 0, NULL);
		}


Interesting though, the following code snippet is the sampling I intend to do, but color remains 0;


layout (set = 1, binding = 1) uniform sampler samplerColorMap;
layout (set = 1, binding = 2) uniform texture2D diffuseTextures[15]; 

...

vec4 color = texture(sampler2D(diffuseTextures[pushConsts.matID], samplerColorMap), inUV);

Yet, if I interpret the sampled image as combined, all(!) scene objects sample texture 0.


layout (set = 1, binding = 1) uniform sampler samplerColorMap;
layout (set = 1, binding = 2) uniform sampler2D diffuseTextures[15]; 

...
 vec4 color = texture(diffuseTextures[pushConsts.matID], inUV);

Do you have some suggestion to my sample?

Best Regards

It appears that creating new VkDescriptorImageInfos from sampler, view and layout does not work.

Fortunately, the samples of Sascha Willems provide a Textureloader that attaches such info to each texture during creation.
Using it, works!

However some suggestion why (or what I have missed) is appreciated.

That leaves the problem of separate images/samplers.

The array of sampler2D[] works! So that answers that question. The array of textures fails and nothing is displayed

Just a small notice: The helper functions from the vkTools::initializer namespace are not intended to be used as a general Vulkan wrapper. They’re just there to keep some Vulkan boilerplate out of the examples and as such are not complete. As Alfonse pointed out they omit several parameters with default values used in all examples. So it’s always a good idea to replace them with explicit calls if something is not working as intended. If you’re looking for a complete Vulkan wrapper, you may take a look at vkcpp from NVIDIA. If you want to see it in action, Brad Davis has forked my samples and updated them to use that wrapper. You can find his fork over here.

Thank you for these links.
And thank you for your samples, your wrappers made the inital learning stage much less scary.

However, I seem to have exceeded the scope of existing samples, as much as an understanding of vulkan ressource management still exceeds my scope.

I want to create a vkCmdDrawIndexedIndirect buffer, but don’t know how to upload it yet.
i would guess it is similar to the ubo buffers uploads.
I will look into that.

Buffer related: Should uniform buffers be staged? I have my materials in an ubo and map it for upload. I tried staging it akin to the vertex and index data. However I could not get it working.

Best Regards

[QUOTE=Christoph;40510]I want to create a vkCmdDrawIndexedIndirect buffer, but don’t know how to upload it yet.
i would guess it is similar to the ubo buffers uploads.
I will look into that.[/quote]

Yes, uploading is the same as for all other buffers using the VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT flag.

Brad has an example for that (CPU side). An indirect rendering example is on my todo list. My plan is to have one that does the indirect buffers on the CPU and one that updates them on the GPU for e.g. doing GPU based culling and stuff, but I’m currently kinda short of (spare) time to work on those.

Depends. For examples and small UBOs it should be fine to leave them host mapped, but in a real world application with some bigger UBOs you might want to do staging for better performance. The process is the same as for vertex buffers or image uploads. But note that you probably won’t gain much by copying if you do it in sync, so try to do the UBO copies asynchronous and make use of dedicated transfer queues on devices that support them. In general it should be a good practice having separate queues fro graphics, compute and transfers.

I would like to provide you with my sample.

I have combined the scene rendering with the omni shadow and am currently implementing the indirect stuff.

I appear to have problems properly staging buffers.

Mapping directly works well. Staging fails

Could you maybe point out my mistake.
Thank you!
Best Regards.



		void* commandBufferMapped;
		VkMemoryAllocateInfo memAlloc = vkTools::initializers::memoryAllocateInfo();
		VkMemoryRequirements memReqs;
		int commandSize = sizeof(VkDrawIndexedIndirectCommand)*indirectDrawCommands.size();
		if(true)
		{ 

			VkBufferCreateInfo bufferCreateInfo = vkTools::initializers::bufferCreateInfo(VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT, commandSize);//VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, 
			VK_CHECK_RESULT(vkCreateBuffer(device, &bufferCreateInfo, nullptr, &indirectDrawCommandsBuffer));
			vkGetBufferMemoryRequirements(device, indirectDrawCommandsBuffer, &memReqs);
			memAlloc.allocationSize = memReqs.size;
			memAlloc.memoryTypeIndex = getMemoryTypeIndex(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT| VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
			VK_CHECK_RESULT(vkAllocateMemory(device, &memAlloc, nullptr, &indirectDrawCommandsMemory));

			VK_CHECK_RESULT(vkMapMemory(device, indirectDrawCommandsMemory, 0, commandSize, 0, &commandBufferMapped));
			memcpy(commandBufferMapped, indirectDrawCommands.data(), commandSize);
			vkUnmapMemory(device, indirectDrawCommandsMemory);

			VK_CHECK_RESULT(vkBindBufferMemory(device, indirectDrawCommandsBuffer, indirectDrawCommandsMemory, 0));
			 
		}
		else
		{			
			struct
			{
				struct {
					VkDeviceMemory memory;
					VkBuffer buffer;
				} cBuffer;
			} staging;

			// Generate vertex buffer
			VkBufferCreateInfo cBufferInfo; 
			 
			// Staging buffer
			cBufferInfo = vkTools::initializers::bufferCreateInfo(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, commandSize);
			VK_CHECK_RESULT(vkCreateBuffer(device, &cBufferInfo, nullptr, &staging.cBuffer.buffer));
			vkGetBufferMemoryRequirements(device, staging.cBuffer.buffer, &memReqs);
			memAlloc.allocationSize = memReqs.size;
			memAlloc.memoryTypeIndex = getMemoryTypeIndex(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT );
			VK_CHECK_RESULT(vkAllocateMemory(device, &memAlloc, nullptr, &staging.cBuffer.memory));
			VK_CHECK_RESULT(vkMapMemory(device, staging.cBuffer.memory, 0, commandSize, 0, &commandBufferMapped));
			memcpy(commandBufferMapped, indirectDrawCommands.data(), commandSize);
			vkUnmapMemory(device, staging.cBuffer.memory);
			VK_CHECK_RESULT(vkBindBufferMemory(device, staging.cBuffer.buffer, staging.cBuffer.memory, 0));
		 
			// Target
			cBufferInfo = vkTools::initializers::bufferCreateInfo(VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, commandSize);
			VK_CHECK_RESULT(vkCreateBuffer(device, &cBufferInfo, nullptr, &indirectDrawCommandsBuffer));
			vkGetBufferMemoryRequirements(device, indirectDrawCommandsBuffer, &memReqs);
			memAlloc.allocationSize = memReqs.size;
			memAlloc.memoryTypeIndex = getMemoryTypeIndex(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
			VK_CHECK_RESULT(vkAllocateMemory(device, &memAlloc, nullptr, &indirectDrawCommandsMemory));
			VK_CHECK_RESULT(vkBindBufferMemory(device, indirectDrawCommandsBuffer, indirectDrawCommandsMemory, 0));
		 

			// Copy
			VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo();
			VK_CHECK_RESULT(vkBeginCommandBuffer(copyCmd, &cmdBufInfo));

			VkBufferCopy copyRegion = {};

			copyRegion.size = commandSize;
			vkCmdCopyBuffer(
				 copyCmd,
				 staging.cBuffer.buffer,
				 indirectDrawCommandsBuffer,
				 1,
				 &copyRegion);
			 

			VkSubmitInfo submitInfo = {};
			submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
			submitInfo.commandBufferCount = 1;
			submitInfo.pCommandBuffers = &copyCmd;

		 	VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));
		 	VK_CHECK_RESULT(vkQueueWaitIdle(queue));

			vkDestroyBuffer(device, staging.cBuffer.buffer, nullptr);
			vkFreeMemory(device, staging.cBuffer.memory, nullptr);

		}



I have sucessfully implemented the multidraw indirect, vulkan supports it natively, it just not advertises the multi capability of indirect in the name.

However, how can I implement the “gl_drawID” functionality. push_constant does not work if I blast the draw commands in a single call.
Iterating over the indirect buffer one sub mesh at a time works but is ultimately not “multi”. :slight_smile:

[QUOTE=Christoph;40514]However, how can I implement the “gl_drawID” functionality. push_constant does not work if I blast the draw commands in a single call.
Iterating over the indirect buffer one sub mesh at a time works but is ultimately not “multi”. :)[/QUOTE]

A bit short on time, so no details, but gl_drawID is pretty much (gl_InstanceIndex / divisor) + baseInstance afair. gl_InstanceIndex was gl_InstanceID in OpenGL. So with that formula it should be easy to calculate.

Christophe Riccio has a nice article on “Surviving without gl_DrawID” btw :wink:

But you’re free to pass your own draw ID with a vertex attribute that increases with instance (VK_VERTEX_INPUT_RATE_VERTEX).

vulkan supports it natively, it just not advertises the multi capability of indirect in the name.

Actually, it does not. That’s a specific feature which you have to request. If multiDrawIndirect is not requested in your features, then maxIndirectDrawCount will be 1.

gl_drawID is pretty much (gl_InstanceIndex / divisor) + baseInstance afair. gl_InstanceIndex was gl_InstanceID in OpenGL. So with that formula it should be easy to calculate.

That’s wrong in several ways. gl_InstanceID is not gl_InstanceIndex. That’s why they changed the name. gl_InstanceID did not include the baseInstance from the draw command, while gl_InstanceIndex always does.

Also, gl_drawID has nothing at all to do with instancing. It is a count of the number of drawing commands in a multidraw command. You cannot compute it from instances alone, because each draw command can have a different instance count.

Even the article you cited doesn’t work in the presence of instanced rendering within a drawing command (that is, if one of the multidraw commands uses instancing to do, you know, actual mesh instancing). It basically hijacks instancing to be used only for DrawId.

It’d be better to just wait until glDrawID is actually added to Vulkan as a conditional feature.