Non uniform texture access in vulkan glsl - c++

I am trying to write a compute shader that raytraces an image, pixels on the right of the yz plane sample from image A, those on the left from image B.
I don't want to have to sample both images so I am trying to use non uniform access by doing:
texture(textures[nonuniformEXT(sampler_id)], vec2(0.5));
and enabling the relevant extension in the shader. This triggers the following validaiton layer error:
Message: Validation Error: [ VUID-VkShaderModuleCreateInfo-pCode-01091 ] Object 0: handle = 0x55a1c21315d0, name = Logical device: AMD RADV RAVEN2, type = VK_OBJECT_TYPE_DEVICE; | MessageID = 0xa7bb8db6 | vkCreateShaderModule(): The SPIR-V Capability (SampledImageArrayNonUniformIndexing) was declared, but none of the requirements were met to use it. The Vulkan spec states: If pCode declares any of the capabilities listed in the SPIR-V Environment appendix, one of the corresponding requirements must be satisfied (https://vulkan.lunarg.com/doc/view/1.2.182.0/linux/1.2-extensions/vkspec.html#VUID-VkShaderModuleCreateInfo-pCode-01091)
If I read the docs it would seem this is a hardware feature, but someone said I can still have non uniform access if create the correct extension object. But I am not entirely sure how to do that.

You have to enable the feature at device creation.
You can check for support of the feature by calling vkGetPhysicalDeviceFeatures2 and following the pNext chain through to a VkPhysicalDeviceVulkan12Features, and checking that shaderSampledImageArrayNonUniformIndexing member is to VK_TRUE.
After that when creating the device with vkCreateDevice, inside the pCreateInfo structure, in the pNext chain you have to have a VkPhysicalDeviceVulkan12Features with shaderSampledImageArrayNonUniformIndexing set to VK_TRUE.
bool checkForNonUniformIndexing(VkPhysicalDevice physicalDevice)
{
VkPhysicalDeviceFeatures2 features;
vkGetPhysicalDeviceFeatures2(physicalDevice, &features);
if(features.sType != VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2)
{
return false;
}
const VkPhysicalDeviceFeatures2* next = &features;
do
{
// We know the type of the struct based on the `sType` member, but the first
// two fields are the same in all of these structs. There may be a more appropriate
// generic structure to use, but as long as we don't access any further members
// we should be mostly fine.
next = reinterpret_cast<const VkPhysicalDeviceFeatures*>(next->pNext);
if(next.sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES)
{
const VkPhysicalDeviceVulkan12Features* pVk12Features = reinterpret_cast<const VkPhysicalDeviceVulkan12Features*>(next);
return next.shaderSampledImageArrayNonUniformIndexing == VK_TRUE;
}
} while(next);
return false;
}
VkDevice* createDevice(VkPhysicalDevice physicalDevice, const VkAllocationCallbacks* pAllocator)
{
VkPhysicalDeviceVulkan12Features features;
features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
features.shaderSampledImageArrayNonUniformIndexing = VK_TRUE;
VkDeviceCreateInfo createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
createInfo.pNext = &features;
// Setting other create data
VkDevice device;
vkCreateDevice(physicalDevice, &createInfo, pAllocator, &device);
// Error checking
return device;
}

Related

Using Vulkan memory allocator with Volk

I'm currently trying to use Vulkan memory allocator with the meta loader Volk
here is the link of the two: https://github.com/zeux/volk
https://gpuopen.com/vulkan-memory-allocator/
But I have trouble with creating the VmaAllocator, here is my code:
void VulkApp::Init(PipelineFlags t_Conf, entt::registry& t_reg)
{
volkInitialize();
WindowProps props = {};
props.Height = 600;
props.Width = 800;
props.Title = "Engine";
currentWindow = EngWindow::Create(props);
//Create all physical, logical, instance for vulkan
Prepare();
VolkDeviceTable test;
volkLoadDeviceTable(&test, m_LogicalDevice->GetVkDevice());
VmaAllocatorCreateInfo allocatorCreateInfo = {};
allocatorCreateInfo.vulkanApiVersion = VK_API_VERSION_1_2;
allocatorCreateInfo.physicalDevice = m_PhysicalDevice->GetVkPhysicalDevice();
allocatorCreateInfo.device = m_LogicalDevice->GetVkDevice();
allocatorCreateInfo.instance = m_Instance->GetRawVkInstance();
allocatorCreateInfo.pVulkanFunctions = reinterpret_cast<const VmaVulkanFunctions*> (&test);
VmaAllocator allocator;
vmaCreateAllocator(&allocatorCreateInfo, &allocator);
BuildRenderPipelines(t_Conf, t_reg);
}
void VulkApp::Prepare()
{
m_Instance = std::make_unique<VulkInst>();
volkLoadInstance(m_Instance->GetRawVkInstance());
currentWindow->CreateSurface(m_Instance->GetRawVkInstance(), &m_Surface);
m_PhysicalDevice = std::make_unique<PhysicalDevice>(*m_Instance);
m_LogicalDevice = std::make_unique<LogicalDevice>(*m_Instance, *m_PhysicalDevice, m_Surface);
volkLoadDevice(m_LogicalDevice->GetVkDevice());
m_SwapChain = std::make_unique<SwapChain>(ChooseSwapExtent(), *m_LogicalDevice, *m_PhysicalDevice, m_Surface);
GraphicsHelpers::CreateCommandPool(m_CommandPool, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT); //TODO: added Transient bit
CreateFrameSyncResources();
/*
Create Camera
*/
BuildSwapChainResources();
}
I don't have any error when i build, but when i execute, the VmaCreateAllocator return an error:
Exception raised at 0x00007FFAB0CD836B (VkLayer_khronos_validation.dll) in My_Game.exe : 0xC0000005 : access violation reading location 0x0000000000000120.
Not very useful, but it stops on the line 14082 of the file vk_mem_alloc.h:
(*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
The program check all the vulkan validation function so my vulkan function table must be good. But still the allocation fail.
I'm sorry i don't put a 'minimal' code, but with vulkan, even the minimum is really long. So, as a first test, maybe some of you have an insight of the error?
If you use a loader like Volk, you need to provide all memory related Vulkan function pointers used by VMA yourself.
This is done via the pVulkanFunctions member of the VmaAllocatorCreateInfo structure.
So when creating your VmaAllactor you set the function pointer in that to those fetched via Volk like this:
VmaVulkanFunctions vma_vulkan_func{};
vma_vulkan_func.vkAllocateMemory = vkAllocateMemory;
vma_vulkan_func.vkBindBufferMemory = vkBindBufferMemory;
vma_vulkan_func.vkBindImageMemory = vkBindImageMemory;
vma_vulkan_func.vkCreateBuffer = vkCreateBuffer;
vma_vulkan_func.vkCreateImage = vkCreateImage;
vma_vulkan_func.vkDestroyBuffer = vkDestroyBuffer;
vma_vulkan_func.vkDestroyImage = vkDestroyImage;
vma_vulkan_func.vkFlushMappedMemoryRanges = vkFlushMappedMemoryRanges;
vma_vulkan_func.vkFreeMemory = vkFreeMemory;
vma_vulkan_func.vkGetBufferMemoryRequirements = vkGetBufferMemoryRequirements;
vma_vulkan_func.vkGetImageMemoryRequirements = vkGetImageMemoryRequirements;
vma_vulkan_func.vkGetPhysicalDeviceMemoryProperties = vkGetPhysicalDeviceMemoryProperties;
vma_vulkan_func.vkGetPhysicalDeviceProperties = vkGetPhysicalDeviceProperties;
vma_vulkan_func.vkInvalidateMappedMemoryRanges = vkInvalidateMappedMemoryRanges;
vma_vulkan_func.vkMapMemory = vkMapMemory;
vma_vulkan_func.vkUnmapMemory = vkUnmapMemory;
vma_vulkan_func.vkCmdCopyBuffer = vkCmdCopyBuffer;
And then pass those to the create info:
VmaAllocatorCreateInfo allocator_info{};
...
allocator_info.pVulkanFunctions = &vma_vulkan_func;

Vulkan validation layers complain about extension not enabled, code works as if extension was enabled

I am trying to use the dynamic rendering extension, to that effect I am initializing my instance this way:
auto enabled = vk::ValidationFeatureEnableEXT::eBestPractices;
vk::ValidationFeaturesEXT features;
features.enabledValidationFeatureCount = 1;
features.pEnabledValidationFeatures = &enabled;
vk::PhysicalDeviceDynamicRenderingFeaturesKHR dynamic_rendering = {};
dynamic_rendering.dynamicRendering = true;
features.pNext = &dynamic_rendering;
// Setup general information about the current application.
vk::ApplicationInfo program_info(
"NeverEngine",
VK_MAKE_VERSION(1, 0, 0),
"No Engine",
VK_MAKE_VERSION(1, 0, 0),
VK_API_VERSION_1_2);
// Create Vulkan instance to communicate with the loader.
vk::InstanceCreateInfo create_info = {};
create_info.pNext = &features;
create_info.pApplicationInfo = &program_info,
create_info.enabledLayerCount = static_cast<uint32_t>(VALIDATION_LAYERS.size()),
create_info.ppEnabledLayerNames = VALIDATION_LAYERS.data(),
create_info.enabledExtensionCount = static_cast<uint32_t>(required_extensions.size()),
create_info.ppEnabledExtensionNames = required_extensions.data();
auto [result, instance] = vk::createInstanceUnique(create_info);
Assert(result == vk::Result::eSuccess, "Error: Failed to create instance");
However I am getting these errors:
VUID-VkInstanceCreateInfo-pNext-pNext(ERROR / SPEC): msgNum: -1337267667 - Validation Error: [ VUID-VkInstanceCreateInfo-pNext-pNext ] Object 0: VK_NULL_HANDLE, type = VK_OBJECT_TYPE_DEVICE; | MessageID = 0xb04aea2d | vkCreateInstance: pCreateInfo->pNext chain includes a structure with unexpected VkStructureType VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES; Allowed structures are [VkDebugReportCallbackCreateInfoEXT, VkDebugUtilsMessengerCreateInfoEXT, VkValidationFeaturesEXT, VkValidationFlagsEXT]. This error is based on the Valid Usage documentation for version 204 of the Vulkan header. It is possible that you are using a struct from a private extension or an extension that was added to a later version of the Vulkan header, in which case the use of pCreateInfo->pNext is undefined and may not work correctly with validation enabled The Vulkan spec states: Each pNext member of any structure (including this one) in the pNext chain must be either NULL or a pointer to a valid instance of VkDebugReportCallbackCreateInfoEXT, VkDebugUtilsMessengerCreateInfoEXT, VkValidationFeaturesEXT, or VkValidationFlagsEXT (https://vulkan.lunarg.com/doc/view/1.3.204.1/linux/1.3-extensions/vkspec.html#VUID-VkInstanceCreateInfo-pNext-pNext)
Objects: 1
[0] 0, type: 3, name: NULL
VUID-VkInstanceCreateInfo-pNext-pNext(ERROR / SPEC): msgNum: -1337267667 - Validation Error: [ VUID-VkInstanceCreateInfo-pNext-pNext ] Object 0: VK_NULL_HANDLE, type = VK_OBJECT_TYPE_INSTANCE; | MessageID = 0xb04aea2d | vkCreateInstance: Includes a pNext pointer (pCreateInfo->pNext) to a VkStructureType (VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES), but its parent extension VK_KHR_dynamic_rendering has not been enabled. The Vulkan spec states: Each pNext member of any structure (including this one) in the pNext chain must be either NULL or a pointer to a valid instance of VkDebugReportCallbackCreateInfoEXT, VkDebugUtilsMessengerCreateInfoEXT, VkValidationFeaturesEXT, or VkValidationFlagsEXT (https://vulkan.lunarg.com/doc/view/1.3.204.1/linux/1.3-extensions/vkspec.html#VUID-VkInstanceCreateInfo-pNext-pNext)
Objects: 1
[0] 0, type: 1, name: NULL
My driver, headers and validation layers are the latest version 1.3.204.
If I don;t enable layers and just run my code, things seem to work fine, I can resize my window and rendering is updated as expected. Is this a bug in the validation layers?
Some research suggests I should do this instead:
const std::vector<const char*> device_extensions = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME,
VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME,
};
vk::PhysicalDeviceDynamicRenderingFeaturesKHR dynamic_rendering = {};
dynamic_rendering.dynamicRendering = true;
// Get the features of the selected device.
vk::PhysicalDeviceFeatures device_features = phys_device.getFeatures();
// The enabledLayerCount and ppEnabledLayerNames parameters are deprecated,
// they should always be 0 and nullptr.
// https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/VkDeviceCreateInfo.html
vk::DeviceCreateInfo create_info = {};
create_info.pNext = &dynamic_rendering;
create_info.queueCreateInfoCount = queue_create_infos.size();
create_info.pQueueCreateInfos = queue_create_infos.data();
create_info.enabledLayerCount = 0;
create_info.ppEnabledLayerNames = nullptr;
create_info.enabledExtensionCount = device_extensions.size();
create_info.ppEnabledExtensionNames = device_extensions.data();
create_info.pEnabledFeatures = &device_features;
But this is returning the following error:
Message ID name: VUID-VkGraphicsPipelineCreateInfo-dynamicRendering-06052
Message: Validation Error: [ VUID-VkGraphicsPipelineCreateInfo-dynamicRendering-06052 ] Object 0: handle = 0x2357fc0, name = Logical device: NVIDIA GeForce GTX 1070, type = VK_OBJECT_TYPE_DEVICE; | MessageID = 0xdf901c9e | vkCreateGraphicsPipeline: pCreateInfos[0].renderPass is VK_NULL_HANDLE but dynamicRendering is not enabled. The Vulkan spec states: If the dynamicRendering feature is not enabled, renderPass must not be VK_NULL_HANDLE (https://vulkan.lunarg.com/doc/view/1.3.204.1/linux/1.3-extensions/vkspec.html#VUID-VkGraphicsPipelineCreateInfo-dynamicRendering-06052)
I don;t understand, I AM enabling the extension.
Following the advice in teh comments, I am seeing this:
vkCreateDevice(physicalDevice, pCreateInfo, pAllocator, pDevice) returns VkResult VK_SUCCESS (0):
physicalDevice: VkPhysicalDevice = 0x32f85c0
pCreateInfo: const VkDeviceCreateInfo* = 0x7fff0d9dbb20:
sType: VkStructureType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO (3)
pNext: const void* = NULL
Which seems to suggest the pNext is not being set properly.
You'll probably need to add VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME to your list of device_extensions. You didn't show the code that sets up that list. The rest of it looks OK.
It isn't that unusual for a driver to go ahead and use a requested feature, even though it isn't enabled. But since that can change from driver to driver, it is always best to enable the extensions and features you need.

Vulkan CommandBuffer in use after vkDeviceWait Idle

During initialization of my program I want to use some single time command buffers for image layout transitions and acceleration structure building etc.
However, I can't seem to free the command buffer once it's finished.
VkCommandBuffer AppContext::singleTimeCommandBuffer() const {
VkCommandBuffer ret;
auto allocInfo = vks::initializers::commandBufferAllocateInfo(vkCommandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1);
vkCheck(vkAllocateCommandBuffers(vkDevice, &allocInfo, &ret));
auto beginInfo = vks::initializers::commandBufferBeginInfo();
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
vkCheck(vkBeginCommandBuffer(ret, &beginInfo));
return ret;
}
void AppContext::endSingleTimeCommands(VkCommandBuffer cmdBuffer) const {
vkCheck(vkEndCommandBuffer(cmdBuffer));
auto submitInfo = vks::initializers::submitInfo(&cmdBuffer);
vkQueueSubmit(queues.graphics, 1, &submitInfo, VK_NULL_HANDLE);
vkQueueWaitIdle(queues.graphics);
// Overkill, I know
vkDeviceWaitIdle(vkDevice);
vkFreeCommandBuffers(vkDevice, vkCommandPool, 1, &cmdBuffer);
}
Which produces the following validation error:
VUID-vkFreeCommandBuffers-pCommandBuffers-00047(ERROR / SPEC): msgNum: 448332540 - Validation Error: [ VUID-vkFreeCommandBuffers-pCommandBuffers-00047 ] Object 0: handle = 0x5586acaeff78, type = VK_OBJECT_TYPE_COMMAND_BUFFER; | MessageID = 0x1ab902fc | Attempt to free VkCommandBuffer 0x5586acaeff78[] which is in use. The Vulkan spec states: All elements of pCommandBuffers must not be in the pending state (https://vulkan.lunarg.com/doc/view/1.2.182.0/linux/1.2-extensions/vkspec.html#VUID-vkFreeCommandBuffers-pCommandBuffers-00047)
Objects: 1
[0] 0x5586acaeff78, type: 6, name: NULL
I don't see how this makes sense since the VkQueueWaitIdle as well as the vkDeviceWaitIdle should ensure the command buffer to not be in the pending state. Am I misunderstanding the Vulkan specs or might I have stumbled upon a bug in the video driver, or perhaps the validation layer?
You aren't checking the return values of vkQueueSubmit(), vkQueueWaitIdle() or vkDeviceWaitIdle(). Are any of them failing? That could cause this error.

How do I extract a GL texture id from a BufferRef using the gstreamer crates?

I am working on a tool to use GL to render frames from a video onto a texture-mapped mesh. I already have a GL app working with a single image (PNG). Now I am trying to use gstreamer to decode the video.
I started with the appsink example.
I have gotten as far as piping the decoded video through glupload into an appsink. Now I need to convert the BufferRef I get from appsink.pull_sample().get_buffer() into a GL texture id (a u32) so I can pass it to GL functions like gl::BindTexture(gl::TEXTURE_2D, tex). I used set_caps() on the appsink to ensure the buffer has feature memory:GLMemory, so it better be a texture and not off-GPU.
How do I extract a GL texture id from a BufferRef using Rust's gstreamer and gstreamer-* crates?
Retrieving the texture from a GstGLMemory in C requires mapping the GstGLMemory itself with a special GST_MAP_GL flag. That specific interface for mapping an OpenGL texture does not currently have an analogue in rust yet. There is some work in a related area within https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/-/merge_requests/581 to help improve the situation with GStreamer OpenGL usage in rust.
If you only need readable access to the texture, there is an extension trait VideoFrameGLExt on VideoFrame that can get you access to the OpenGL texture. There is a usage of VideoFrameGLExt in the glupload example in the gstreamer-rs repository available from https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/-/blob/master/examples/src/bin/glupload.rs. The VideoFrameGLExt trait is currently implemented within https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/-/blob/master/gstreamer-gl/src/gl_video_frame.rs
Something like the following should work for read-only access:
// buffer: gst::Buffer
// info: gst::VideoInfo
if let Ok(frame) = gst_video::VideoFrame::from_buffer_readable_gl(buffer, &info) {
if let Some(texture) = frame.get_texture_id(0) {
// use texture somehow
}
}
If instead you also need to write to the texture, that is currently not exposed and manual bindings would need to be written.
The code I eventually got working was
fn get_gl_memory(bref: &BufferRef, idx: u32) -> Option<*mut GstGLMemory> {
unsafe {
let n = gst_sys::gst_buffer_n_memory(bref.as_ptr() as *mut _);
if idx >= n {
return None;
}
let mem = gst_sys::gst_buffer_peek_memory(bref.as_ptr() as *mut _, idx);
if 0 != gst_gl_sys::gst_is_gl_memory(mem) {
Some(mem as *mut _)
} else {
None
}
}
}
//
let gl_mem = get_gl_memory(buffer, 0).unwrap();
let gl_mem = unsafe { &*gl_mem };
let tex_id = gl_mem.tex_id;
although the solution from ystreet00 works great if you have convenient access to the gst::VideoInfo.

Is there anyway to set the VkDescriptorImageInfo to null or have some way of skipping using a VkWriteDescriptorSet without vulkan complaining

Some of the mesh that I'll be using doesn't always have a DiffuseMap or a SpecularMap. When I try to load something without a diffuse and specular map the program crashes because there's nothing in the DiffuseMap.ImageView/SpecularMap.ImageView because it isn't pointing to anything. If I try to set the imageview/sample to VK_NULL_HANDLE the program gives me this and crashes at the vkUpdateDescriptorSets:
Validation Layer: Invalid VkImageView Object 0x0. The Vulkan spec states: If descriptorType is VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, or VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, the imageView and imageLayout members of each element of pImageInfo must be a valid VkImageView and VkImageLayout, respectively (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-VkWriteDescriptorSet-descriptorType-00326)
Then if I just try set the binding to null, I get this:
Validation Layer: vkUpdateDescriptorSets: required parameter pDescriptorWrites[2].dstSet specified as VK_NULL_HANDLE
Validation Layer: Cannot call vkUpdateDescriptorSets() on VkDescriptorSet 0x0[] that has not been allocated.
This is what the base code looks like right now. This is the area that defines the descriptor sets to make it bit easier to see what's going on:
void Mesh::CreateDescriptorSets(VulkanRenderer& Renderer)
{
BaseMesh::CreateDescriptorSets(Renderer, *GetDescriptorSetLayout(Renderer));
VkDescriptorImageInfo DiffuseMap = {};
DiffuseMap.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
DiffuseMap.imageView = TextureList[0].textureImageView;
DiffuseMap.sampler = TextureList[0].textureSampler;
VkDescriptorImageInfo SpecularMap = {};
SpecularMap.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
SpecularMap.imageView = TextureList[1].textureImageView;
SpecularMap.sampler = TextureList[1].textureSampler;
for (size_t i = 0; i < GetSwapChainImageCount(Renderer); i++)
{
VkDescriptorBufferInfo PositionInfo = {};
PositionInfo.buffer = uniformBuffers[i];
PositionInfo.offset = 0;
PositionInfo.range = sizeof(UniformBufferObject);
VkDescriptorBufferInfo AmbiantLightInfo = {};
AmbiantLightInfo.buffer = AmbientLightUniformBuffers[i];
AmbiantLightInfo.offset = 0;
AmbiantLightInfo.range = sizeof(AmbientLightUniformBuffer);
VkDescriptorBufferInfo LightInfo = {};
LightInfo.buffer = LighterUniformBuffers[i];
LightInfo.offset = 0;
LightInfo.range = sizeof(Lighter);
std::array<WriteDescriptorSetInfo, 5> WriteDescriptorInfo = {};
WriteDescriptorInfo[0].DstBinding = 0;
WriteDescriptorInfo[0].DstSet = descriptorSets[i];
WriteDescriptorInfo[0].DescriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
WriteDescriptorInfo[0].DescriptorBufferInfo = PositionInfo;
WriteDescriptorInfo[1].DstBinding = 1;
WriteDescriptorInfo[1].DstSet = descriptorSets[i];
WriteDescriptorInfo[1].DescriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
WriteDescriptorInfo[1].DescriptorImageInfo = DiffuseMap;
WriteDescriptorInfo[2].DstBinding = 2;
WriteDescriptorInfo[2].DstSet = descriptorSets[i];
WriteDescriptorInfo[2].DescriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
WriteDescriptorInfo[2].DescriptorImageInfo = SpecularMap;
WriteDescriptorInfo[3].DstBinding = 3;
WriteDescriptorInfo[3].DstSet = descriptorSets[i];
WriteDescriptorInfo[3].DescriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
WriteDescriptorInfo[3].DescriptorBufferInfo = AmbiantLightInfo;
WriteDescriptorInfo[4].DstBinding = 4;
WriteDescriptorInfo[4].DstSet = descriptorSets[i];
WriteDescriptorInfo[4].DescriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
WriteDescriptorInfo[4].DescriptorBufferInfo = LightInfo;
Mesh::CreateDescriptorSetsData(Renderer, std::vector<WriteDescriptorSetInfo>(WriteDescriptorInfo.begin(), WriteDescriptorInfo.end()));
}
}
Until Vulkan 1.2, Vulkan did not recognize the possibility of a descriptor being "empty". When a descriptor set is created, the descriptors are (mostly) uninitialized. It's OK to have a set with an uninitialized descriptor, so long as the pipeline which consumes it does not statically use the descriptor. Since you are presumably trying to use the same pipeline for objects with diffuse maps and objects without them, your shader reads from the image based on a variable you provide. That represents static use of the descriptor, so you need an image there.
The typical way to deal with this is to create a tiny image of a reasonable format and stuff that into the descriptor. You can use the same image for essentially any "null" texture you want to use.
Vulkan 1.2, as part of the VK_EXT_descriptor_indexing extension promoted to core, allows for the possibility of partially bound descriptors. Basically, if the descriptorBindingPartiallyBound feature is available and requested, then you can allocate a descriptor set using the VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT bit. This means that it's OK to leave a descriptor undefined so long as it is not dynamically used.
So you simply wouldn't write a value for that descriptor.
Of course, this requires 1.2 (or the aforementioned extension), as well as requesting the feature.
You are not quite correct, a combined image sampler still requires a valid sampler for some reason, and not only that, a nullDescriptor functionality have to be enabled. I run into exactly the same problem. What noone ever tells is that partly bound descriptors is not the same as sparsly bound descriptors, so it basicaly means y can bind X descriptors out of N if X<N but all those X must be valid, the specs is realy thin on this and there is no good examples.