Browse Source

Add some code for bindless texture & images

Panagiotis Christopoulos Charitos 6 years ago
parent
commit
72873c548c

+ 3 - 0
src/anki/gr/CommandBuffer.h

@@ -264,6 +264,9 @@ public:
 	/// Bind texture buffer.
 	void bindTextureBuffer(U32 set, U32 binding, BufferPtr buff, PtrSize offset, PtrSize range, Format fmt);
 
+	/// Bind the bindless descriptor set into a slot.
+	void bindBindless(U32 set);
+
 	/// Set push constants.
 	void setPushConstants(const void* data, U32 dataSize);
 

+ 2 - 3
src/anki/gr/Common.h

@@ -39,11 +39,10 @@ class CommandBufferInitInfo;
 // Some constants
 const U MAX_VERTEX_ATTRIBUTES = 8;
 const U MAX_COLOR_ATTACHMENTS = 4;
-const U MAX_MIPMAPS = 16;
-const U MAX_TEXTURE_LAYERS = 32;
-const U MAX_SPECIALIZED_CONSTS = 64;
 const U MAX_DESCRIPTOR_SETS = 2; ///< Groups that can be bound at the same time.
 const U MAX_BINDINGS_PER_DESCRIPTOR_SET = 32;
+const U MAX_BINDLESS_TEXTURES = 256;
+const U MAX_BINDLESS_IMAGES = 32;
 
 const U MAX_FRAMES_IN_FLIGHT = 3; ///< Triple buffering.
 

+ 3 - 0
src/anki/gr/TextureView.h

@@ -91,6 +91,9 @@ public:
 		return m_subresource;
 	}
 
+	/// Generate an index that can be used to index the view from the global bindless descriptor set.
+	U32 generateBindlessIndex(TextureUsageBit usages);
+
 protected:
 	TextureType m_texType = TextureType::COUNT;
 	TextureSubresourceInfo m_subresource;

+ 9 - 3
src/anki/gr/vulkan/CommandBuffer.cpp

@@ -183,19 +183,19 @@ void CommandBuffer::bindSampler(U32 set, U32 binding, SamplerPtr sampler)
 void CommandBuffer::bindUniformBuffer(U32 set, U32 binding, BufferPtr buff, PtrSize offset, PtrSize range)
 {
 	ANKI_VK_SELF(CommandBufferImpl);
-	self.bindUniformBuffer(set, binding, buff, offset, range);
+	self.bindUniformBufferInternal(set, binding, buff, offset, range);
 }
 
 void CommandBuffer::bindStorageBuffer(U32 set, U32 binding, BufferPtr buff, PtrSize offset, PtrSize range)
 {
 	ANKI_VK_SELF(CommandBufferImpl);
-	self.bindStorageBuffer(set, binding, buff, offset, range);
+	self.bindStorageBufferInternal(set, binding, buff, offset, range);
 }
 
 void CommandBuffer::bindImage(U32 set, U32 binding, TextureViewPtr img)
 {
 	ANKI_VK_SELF(CommandBufferImpl);
-	self.bindImage(set, binding, img);
+	self.bindImageInternal(set, binding, img);
 }
 
 void CommandBuffer::bindTextureBuffer(U32 set, U32 binding, BufferPtr buff, PtrSize offset, PtrSize range, Format fmt)
@@ -203,6 +203,12 @@ void CommandBuffer::bindTextureBuffer(U32 set, U32 binding, BufferPtr buff, PtrS
 	ANKI_ASSERT(!"TODO");
 }
 
+void CommandBuffer::bindBindless(U32 set)
+{
+	ANKI_VK_SELF(CommandBufferImpl);
+	self.bindBindlessInternal(set);
+}
+
 void CommandBuffer::bindShaderProgram(ShaderProgramPtr prog)
 {
 	ANKI_VK_SELF(CommandBufferImpl);

+ 8 - 3
src/anki/gr/vulkan/CommandBufferImpl.h

@@ -254,13 +254,18 @@ public:
 		m_microCmdb->pushObjectRef(sampler);
 	}
 
-	void bindImage(U32 set, U32 binding, TextureViewPtr& img)
+	void bindImageInternal(U32 set, U32 binding, TextureViewPtr& img)
 	{
 		commandCommon();
 		m_dsetState[set].bindImage(binding, img.get());
 		m_microCmdb->pushObjectRef(img);
 	}
 
+	void bindBindlessInternal(U32 set)
+	{
+		ANKI_ASSERT(!"TODO");
+	}
+
 	void beginRenderPass(FramebufferPtr fb,
 		const Array<TextureUsageBit, MAX_COLOR_ATTACHMENTS>& colorAttachmentUsages,
 		TextureUsageBit depthStencilAttachmentUsage,
@@ -326,14 +331,14 @@ public:
 
 	void bindShaderProgram(ShaderProgramPtr& prog);
 
-	void bindUniformBuffer(U32 set, U32 binding, BufferPtr& buff, PtrSize offset, PtrSize range)
+	void bindUniformBufferInternal(U32 set, U32 binding, BufferPtr& buff, PtrSize offset, PtrSize range)
 	{
 		commandCommon();
 		m_dsetState[set].bindUniformBuffer(binding, buff.get(), offset, range);
 		m_microCmdb->pushObjectRef(buff);
 	}
 
-	void bindStorageBuffer(U32 set, U32 binding, BufferPtr& buff, PtrSize offset, PtrSize range)
+	void bindStorageBufferInternal(U32 set, U32 binding, BufferPtr& buff, PtrSize offset, PtrSize range)
 	{
 		commandCommon();
 		m_dsetState[set].bindStorageBuffer(binding, buff.get(), offset, range);

+ 11 - 11
src/anki/gr/vulkan/Common.h

@@ -38,17 +38,17 @@ enum class VulkanExtensions : U16
 {
 	NONE = 0,
 	KHR_MAINENANCE1 = 1 << 0,
-	AMD_NEGATIVE_VIEWPORT_HEIGHT = 1 << 1,
-	KHR_XCB_SURFACE = 1 << 2,
-	KHR_WIN32_SURFACE = 1 << 3,
-	KHR_SWAPCHAIN = 1 << 4,
-	KHR_SURFACE = 1 << 5,
-	EXT_DEBUG_MARKER = 1 << 6,
-	NV_DEDICATED_ALLOCATION = 1 << 7,
-	EXT_SHADER_SUBGROUP_BALLOT = 1 << 8,
-	EXT_DEBUG_REPORT = 1 << 9,
-	AMD_SHADER_INFO = 1 << 10,
-	AMD_RASTERIZATION_ORDER = 1 << 11,
+	KHR_XCB_SURFACE = 1 << 1,
+	KHR_WIN32_SURFACE = 1 << 2,
+	KHR_SWAPCHAIN = 1 << 3,
+	KHR_SURFACE = 1 << 4,
+	EXT_DEBUG_MARKER = 1 << 5,
+	KHR_DEDICATED_ALLOCATION = 1 << 6,
+	EXT_SHADER_SUBGROUP_BALLOT = 1 << 7,
+	EXT_DEBUG_REPORT = 1 << 8,
+	AMD_SHADER_INFO = 1 << 9,
+	AMD_RASTERIZATION_ORDER = 1 << 10,
+	EXT_DESCRIPTOR_INDEXING = 1 << 11,
 };
 ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(VulkanExtensions, inline)
 

+ 224 - 0
src/anki/gr/vulkan/DescriptorSet.cpp

@@ -667,4 +667,228 @@ Error DescriptorSetFactory::newDescriptorSet(ThreadId tid,
 	return Error::NONE;
 }
 
+BindlessDescriptorSet::~BindlessDescriptorSet()
+{
+	ANKI_ASSERT(m_dset == VK_NULL_HANDLE);
+	ANKI_ASSERT(m_layout == VK_NULL_HANDLE);
+	ANKI_ASSERT(m_pool == VK_NULL_HANDLE);
+}
+
+Error BindlessDescriptorSet::init(const GrAllocator<U8>& alloc, VkDevice dev)
+{
+	ANKI_ASSERT(dev);
+	m_alloc = alloc;
+	m_dev = dev;
+
+	// Create the layout
+	{
+		Array<VkDescriptorSetLayoutBinding, 2> bindings = {};
+		bindings[0].binding = 0;
+		bindings[0].stageFlags = VK_SHADER_STAGE_ALL;
+		bindings[0].descriptorCount = MAX_BINDLESS_TEXTURES;
+		bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
+		bindings[1].binding = 1;
+		bindings[1].stageFlags = VK_SHADER_STAGE_ALL;
+		bindings[1].descriptorCount = MAX_BINDLESS_IMAGES;
+		bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
+
+		Array<VkDescriptorBindingFlagsEXT, 2> bindingFlags = {};
+		bindingFlags[0] = VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT_EXT
+						  | VK_DESCRIPTOR_BINDING_UPDATE_UNUSED_WHILE_PENDING_BIT_EXT
+						  | VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT_EXT;
+		bindingFlags[1] = bindingFlags[0];
+
+		VkDescriptorSetLayoutBindingFlagsCreateInfoEXT extraInfos = {};
+		extraInfos.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO_EXT;
+		extraInfos.bindingCount = bindingFlags.getSize();
+		extraInfos.pBindingFlags = &bindingFlags[0];
+
+		VkDescriptorSetLayoutCreateInfo ci = {};
+		ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+		ci.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT_EXT;
+		ci.bindingCount = bindings.getSize();
+		ci.pBindings = &bindings[0];
+		ci.pNext = &extraInfos;
+
+		ANKI_VK_CHECK(vkCreateDescriptorSetLayout(m_dev, &ci, nullptr, &m_layout));
+	}
+
+	// Create the pool
+	{
+		Array<VkDescriptorPoolSize, 2> sizes = {};
+		sizes[0].type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
+		sizes[0].descriptorCount = MAX_BINDLESS_TEXTURES;
+		sizes[1].type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
+		sizes[1].descriptorCount = MAX_BINDLESS_IMAGES;
+
+		VkDescriptorPoolCreateInfo ci = {};
+		ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
+		ci.maxSets = 1;
+		ci.poolSizeCount = sizes.getSize();
+		ci.pPoolSizes = &sizes[0];
+		ci.flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT_EXT;
+
+		ANKI_VK_CHECK(vkCreateDescriptorPool(m_dev, &ci, nullptr, &m_pool));
+	}
+
+	// Create the descriptor set
+	{
+		VkDescriptorSetAllocateInfo ci = {};
+		ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+		ci.descriptorPool = m_pool;
+		ci.descriptorSetCount = 1;
+		ci.pSetLayouts = &m_layout;
+
+		ANKI_VK_CHECK(vkAllocateDescriptorSets(m_dev, &ci, &m_dset));
+	}
+
+	// Init the free arrays
+	{
+		m_freeTexIndices.create(m_alloc, MAX_BINDLESS_TEXTURES);
+		m_freeTexIndexCount = m_freeTexIndices.getSize();
+
+		for(U i = 0; i < m_freeTexIndices.getSize(); ++i)
+		{
+			m_freeTexIndices[i] = m_freeTexIndices.getSize() - i - 1;
+		}
+
+		m_freeImgIndices.create(m_alloc, MAX_BINDLESS_IMAGES);
+		m_freeImgIndexCount = m_freeImgIndices.getSize();
+
+		for(U i = 0; i < m_freeImgIndices.getSize(); ++i)
+		{
+			m_freeImgIndices[i] = m_freeImgIndices.getSize() - i - 1;
+		}
+	}
+
+	return Error::NONE;
+}
+
+void BindlessDescriptorSet::destroy()
+{
+	if(m_pool)
+	{
+		vkDestroyDescriptorPool(m_dev, m_pool, nullptr);
+		m_pool = VK_NULL_HANDLE;
+		m_dset = VK_NULL_HANDLE;
+	}
+
+	if(m_layout)
+	{
+		vkDestroyDescriptorSetLayout(m_dev, m_layout, nullptr);
+		m_layout = VK_NULL_HANDLE;
+	}
+
+	m_freeImgIndices.destroy(m_alloc);
+	m_freeTexIndices.destroy(m_alloc);
+}
+
+U32 BindlessDescriptorSet::bindTexture(const VkImageView view, const VkImageLayout layout)
+{
+	ANKI_ASSERT(view);
+	LockGuard<Mutex> lock(m_mtx);
+	ANKI_ASSERT(m_freeTexIndexCount > 0 && "Out of indices");
+
+	// Get the index
+	--m_freeTexIndexCount;
+	const U idx = m_freeTexIndices[m_freeTexIndexCount];
+	ANKI_ASSERT(idx < MAX_BINDLESS_TEXTURES);
+
+	// Update the set
+	VkDescriptorImageInfo imageInf = {};
+	imageInf.imageView = view;
+	imageInf.imageLayout = layout;
+
+	VkWriteDescriptorSet write = {};
+	write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+	write.pNext = nullptr;
+	write.dstSet = m_dset;
+	write.descriptorCount = 1;
+	write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
+	write.dstArrayElement = idx;
+	write.pImageInfo = &imageInf;
+
+	vkUpdateDescriptorSets(m_dev, 1, &write, 0, nullptr);
+
+	return idx;
+}
+
+U32 BindlessDescriptorSet::bindImage(const VkImageView view)
+{
+	ANKI_ASSERT(view);
+	LockGuard<Mutex> lock(m_mtx);
+	ANKI_ASSERT(m_freeImgIndexCount > 0 && "Out of indices");
+
+	// Get the index
+	--m_freeImgIndexCount;
+	const U idx = m_freeImgIndices[m_freeImgIndexCount];
+	ANKI_ASSERT(idx < MAX_BINDLESS_IMAGES);
+
+	// Update the set
+	VkDescriptorImageInfo imageInf = {};
+	imageInf.imageView = view;
+	imageInf.imageLayout = VK_IMAGE_LAYOUT_GENERAL; // Storage images are always in general.
+
+	VkWriteDescriptorSet write = {};
+	write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+	write.pNext = nullptr;
+	write.dstSet = m_dset;
+	write.descriptorCount = 1;
+	write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
+	write.dstArrayElement = idx;
+	write.pImageInfo = &imageInf;
+
+	vkUpdateDescriptorSets(m_dev, 1, &write, 0, nullptr);
+
+	return idx;
+}
+
+void BindlessDescriptorSet::unbindCommon(U32 idx, DynamicArray<U16>& freeIndices, U16& freeIndexCount)
+{
+	ANKI_ASSERT(idx < freeIndices.getSize());
+
+	LockGuard<Mutex> lock(m_mtx);
+	ANKI_ASSERT(freeIndexCount < freeIndices.getSize());
+
+	freeIndices[freeIndexCount] = idx;
+	++freeIndexCount;
+
+	// Sort the free indices to minimize fragmentation
+	std::sort(&freeIndices[0], &freeIndices[0] + freeIndexCount, std::greater<U16>());
+
+	// Make sure there are no duplicates
+	for(U i = 1; i < freeIndexCount; ++i)
+	{
+		ANKI_ASSERT(freeIndices[i] != freeIndices[i - 1]);
+	}
+}
+
+Error BindlessDescriptorSet::initDeviceFeatures(
+	VkPhysicalDevice pdev, VkPhysicalDeviceDescriptorIndexingFeaturesEXT& indexingFeatures)
+{
+	indexingFeatures = {};
+	indexingFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT;
+
+	VkPhysicalDeviceFeatures2 features = {};
+	features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
+	features.pNext = &indexingFeatures;
+
+	vkGetPhysicalDeviceFeatures2(pdev, &features);
+
+	if(!indexingFeatures.descriptorBindingSampledImageUpdateAfterBind
+		|| !indexingFeatures.descriptorBindingStorageImageUpdateAfterBind)
+	{
+		ANKI_VK_LOGE("Update descriptors after bind is not supported by the device");
+		return Error::FUNCTION_FAILED;
+	}
+
+	if(!indexingFeatures.descriptorBindingUpdateUnusedWhilePending)
+	{
+		ANKI_VK_LOGE("Update descriptors while cmd buffer is pending is not supported by the device");
+		return Error::FUNCTION_FAILED;
+	}
+
+	return Error::NONE;
+}
+
 } // end namespace anki

+ 59 - 0
src/anki/gr/vulkan/DescriptorSet.h

@@ -129,6 +129,11 @@ class DescriptorSetState
 	friend class DescriptorSetFactory;
 
 public:
+	void makeDirty()
+	{
+		m_anyBindingDirty = true;
+	}
+
 	void setLayout(const DescriptorSetLayout& layout)
 	{
 		if(layout.isCreated())
@@ -255,6 +260,7 @@ private:
 class DescriptorSet
 {
 	friend class DescriptorSetFactory;
+	friend class BindlessDescriptorSet;
 
 public:
 	VkDescriptorSet getHandle() const
@@ -305,6 +311,59 @@ private:
 	DynamicArray<DSLayoutCacheEntry*> m_caches;
 	SpinLock m_cachesMtx; ///< Not a mutex because after a while there will be no reason to lock
 };
+
+/// Wraps a global descriptor set that is used to store bindless textures.
+class BindlessDescriptorSet
+{
+public:
+	~BindlessDescriptorSet();
+
+	Error init(const GrAllocator<U8>& alloc, VkDevice dev);
+
+	void destroy();
+
+	static Error initDeviceFeatures(VkPhysicalDevice pdev, VkPhysicalDeviceDescriptorIndexingFeaturesEXT& features);
+
+	/// Bind a sampled image.
+	U32 bindTexture(const VkImageView view, const VkImageLayout layout);
+
+	/// Bind a storage image.
+	U32 bindImage(const VkImageView view);
+
+	void unbindTexture(U32 idx)
+	{
+		unbindCommon(idx, m_freeTexIndices, m_freeTexIndexCount);
+	}
+
+	void unbindImage(U32 idx)
+	{
+		unbindCommon(idx, m_freeImgIndices, m_freeImgIndexCount);
+	}
+
+	DescriptorSet getDescriptorSet() const
+	{
+		ANKI_ASSERT(m_dset);
+		DescriptorSet out;
+		out.m_handle = m_dset;
+		return out;
+	}
+
+private:
+	GrAllocator<U8> m_alloc;
+	VkDevice m_dev = VK_NULL_HANDLE;
+	VkDescriptorSetLayout m_layout = VK_NULL_HANDLE;
+	VkDescriptorPool m_pool = VK_NULL_HANDLE;
+	VkDescriptorSet m_dset = VK_NULL_HANDLE;
+	Mutex m_mtx;
+
+	DynamicArray<U16> m_freeTexIndices;
+	DynamicArray<U16> m_freeImgIndices;
+
+	U16 m_freeTexIndexCount ANKI_DEBUG_CODE(= MAX_U16);
+	U16 m_freeImgIndexCount ANKI_DEBUG_CODE(= MAX_U16);
+
+	void unbindCommon(U32 idx, DynamicArray<U16>& freeIndices, U16& freeIndexCount);
+};
 /// @}
 
 } // end namespace anki

+ 22 - 15
src/anki/gr/vulkan/GrManagerImpl.cpp

@@ -48,6 +48,8 @@ GrManagerImpl::~GrManagerImpl()
 	m_pplineLayoutFactory.destroy();
 	m_descrFactory.destroy();
 
+	m_bindlessDset.destroy();
+
 	m_pplineCache.destroy(m_device, m_physicalDevice, getAllocator());
 
 	m_fences.destroy();
@@ -204,6 +206,9 @@ Error GrManagerImpl::initInternal(const GrManagerInitInfo& init)
 	m_descrFactory.init(getAllocator(), m_device);
 	m_pplineLayoutFactory.init(getAllocator(), m_device);
 
+	// Bindless descriptors
+	ANKI_CHECK(m_bindlessDset.init(m_alloc, m_device));
+
 	return Error::NONE;
 }
 
@@ -508,22 +513,16 @@ Error GrManagerImpl::initDevice(const GrManagerInitInfo& init)
 				m_extensions |= VulkanExtensions::KHR_MAINENANCE1;
 				extensionsToEnable[extensionsToEnableCount++] = VK_KHR_MAINTENANCE1_EXTENSION_NAME;
 			}
-			else if(CString(&extensionInfos[extCount].extensionName[0])
-					== VK_AMD_NEGATIVE_VIEWPORT_HEIGHT_EXTENSION_NAME)
-			{
-				m_extensions |= VulkanExtensions::AMD_NEGATIVE_VIEWPORT_HEIGHT;
-				// Don't add it just yet. Can't enable it at the same time with VK_KHR_maintenance1
-			}
 			else if(CString(extensionInfos[extCount].extensionName) == VK_EXT_DEBUG_MARKER_EXTENSION_NAME
 					&& init.m_config->getNumber("window.debugMarkers"))
 			{
 				m_extensions |= VulkanExtensions::EXT_DEBUG_MARKER;
 				extensionsToEnable[extensionsToEnableCount++] = VK_EXT_DEBUG_MARKER_EXTENSION_NAME;
 			}
-			else if(CString(extensionInfos[extCount].extensionName) == VK_NV_DEDICATED_ALLOCATION_EXTENSION_NAME)
+			else if(CString(extensionInfos[extCount].extensionName) == VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME)
 			{
-				m_extensions |= VulkanExtensions::NV_DEDICATED_ALLOCATION;
-				extensionsToEnable[extensionsToEnableCount++] = VK_NV_DEDICATED_ALLOCATION_EXTENSION_NAME;
+				m_extensions |= VulkanExtensions::KHR_DEDICATED_ALLOCATION;
+				extensionsToEnable[extensionsToEnableCount++] = VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME;
 			}
 			else if(CString(extensionInfos[extCount].extensionName) == VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME)
 			{
@@ -541,20 +540,28 @@ Error GrManagerImpl::initDevice(const GrManagerInitInfo& init)
 				m_extensions |= VulkanExtensions::AMD_RASTERIZATION_ORDER;
 				extensionsToEnable[extensionsToEnableCount++] = VK_AMD_RASTERIZATION_ORDER_EXTENSION_NAME;
 			}
+			else if(CString(extensionInfos[extCount].extensionName) == VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME)
+			{
+				m_extensions |= VulkanExtensions::EXT_DESCRIPTOR_INDEXING;
+				extensionsToEnable[extensionsToEnableCount++] = VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME;
+			}
 		}
 
-		if(!!(m_extensions & VulkanExtensions::KHR_MAINENANCE1))
+		// Check required extensions.
+		if(!(m_extensions & VulkanExtensions::KHR_MAINENANCE1))
 		{
-			m_extensions = m_extensions & ~VulkanExtensions::AMD_NEGATIVE_VIEWPORT_HEIGHT;
+			ANKI_VK_LOGE("%s is required", VK_KHR_MAINTENANCE1_EXTENSION_NAME);
+			return Error::FUNCTION_FAILED;
 		}
-		else if(!!(m_extensions & VulkanExtensions::AMD_NEGATIVE_VIEWPORT_HEIGHT))
+		if(!(m_extensions & VulkanExtensions::EXT_DESCRIPTOR_INDEXING))
 		{
-			extensionsToEnable[extensionsToEnableCount++] = VK_AMD_NEGATIVE_VIEWPORT_HEIGHT_EXTENSION_NAME;
+			ANKI_VK_LOGE("%s is required", VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME);
+			return Error::FUNCTION_FAILED;
 		}
 		else
 		{
-			ANKI_VK_LOGE("VK_KHR_maintenance1 or VK_AMD_negative_viewport_height required");
-			return Error::FUNCTION_FAILED;
+			ANKI_CHECK(BindlessDescriptorSet::initDeviceFeatures(m_physicalDevice, m_descriptorIndexingFeatures));
+			ci.pNext = &m_descriptorIndexingFeatures;
 		}
 
 		ANKI_VK_LOGI("Will enable the following device extensions:");

+ 9 - 0
src/anki/gr/vulkan/GrManagerImpl.h

@@ -18,6 +18,7 @@
 #include <anki/gr/vulkan/SwapchainFactory.h>
 #include <anki/gr/vulkan/PipelineLayout.h>
 #include <anki/gr/vulkan/PipelineCache.h>
+#include <anki/gr/vulkan/DescriptorSet.h>
 #include <anki/util/HashMap.h>
 
 namespace anki
@@ -215,6 +216,11 @@ public:
 
 	void printPipelineShaderInfo(VkPipeline ppline, CString name, ShaderTypeBit stages, U64 hash = 0) const;
 
+	const BindlessDescriptorSet& getBindlessDescriptorSet() const
+	{
+		return m_bindlessDset;
+	}
+
 private:
 	U64 m_frame = 0;
 
@@ -241,6 +247,7 @@ private:
 
 	VkPhysicalDeviceProperties m_devProps = {};
 	VkPhysicalDeviceFeatures m_devFeatures = {};
+	VkPhysicalDeviceDescriptorIndexingFeaturesEXT m_descriptorIndexingFeatures = {};
 
 	PFN_vkDebugMarkerSetObjectNameEXT m_pfnDebugMarkerSetObjectNameEXT = nullptr;
 	PFN_vkCmdDebugMarkerBeginEXT m_pfnCmdDebugMarkerBeginEXT = nullptr;
@@ -294,6 +301,8 @@ private:
 
 	PipelineCache m_pplineCache;
 
+	BindlessDescriptorSet m_bindlessDset;
+
 	Bool m_r8g8b8ImagesSupported = false;
 	Bool m_s8ImagesSupported = false;
 	Bool m_d24S8ImagesSupported = false;

+ 1 - 1
src/anki/gr/vulkan/TextureImpl.cpp

@@ -202,7 +202,7 @@ Error TextureImpl::initImage(const TextureInitInfo& init_)
 {
 	TextureInitInfo init = init_;
 	Bool useDedicatedMemory = !!(init.m_usage & TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE)
-							  && !!(getGrManagerImpl().getExtensions() & VulkanExtensions::NV_DEDICATED_ALLOCATION);
+							  && !!(getGrManagerImpl().getExtensions() & VulkanExtensions::KHR_DEDICATED_ALLOCATION);
 
 	// Check if format is supported
 	Bool supported;