From dda8de09d2cbff83e88dd468865b8e948da4eb3e Mon Sep 17 00:00:00 2001
From: Artur Wasmut <awasmut@uni-koblenz.de>
Date: Tue, 10 Aug 2021 14:41:31 +0200
Subject: [PATCH] [#105] add ugly pbr grass textures for testing. Implement
 ugly variable descriptor count extensions.

---
 include/vkcv/DescriptorConfig.hpp             |  4 +-
 include/vkcv/DescriptorWrites.hpp             |  5 +-
 .../cube/Grass001_1K_AmbientOcclusion.jpg     |  3 ++
 .../resources/cube/Grass001_1K_Color.jpg      |  3 ++
 .../cube/Grass001_1K_Displacement.jpg         |  3 ++
 .../resources/cube/Grass001_1K_Normal.jpg     |  3 ++
 .../resources/cube/Grass001_1K_Roughness.jpg  |  3 ++
 .../resources/shaders/shader.frag             |  7 +--
 .../resources/shaders/shader.vert             | 22 +++++++-
 projects/bindless_textures/src/main.cpp       | 35 ++++++++++++-
 src/vkcv/Context.cpp                          |  1 +
 src/vkcv/DescriptorConfig.cpp                 |  6 ++-
 src/vkcv/DescriptorManager.cpp                | 50 ++++++++++++++++---
 src/vkcv/ShaderProgram.cpp                    | 27 ++++++++--
 14 files changed, 151 insertions(+), 21 deletions(-)
 create mode 100644 projects/bindless_textures/resources/cube/Grass001_1K_AmbientOcclusion.jpg
 create mode 100644 projects/bindless_textures/resources/cube/Grass001_1K_Color.jpg
 create mode 100644 projects/bindless_textures/resources/cube/Grass001_1K_Displacement.jpg
 create mode 100644 projects/bindless_textures/resources/cube/Grass001_1K_Normal.jpg
 create mode 100644 projects/bindless_textures/resources/cube/Grass001_1K_Roughness.jpg

diff --git a/include/vkcv/DescriptorConfig.hpp b/include/vkcv/DescriptorConfig.hpp
index 767492eb..0fadd75d 100644
--- a/include/vkcv/DescriptorConfig.hpp
+++ b/include/vkcv/DescriptorConfig.hpp
@@ -41,12 +41,14 @@ namespace vkcv
             uint32_t bindingID,
             DescriptorType descriptorType,
             uint32_t descriptorCount,
-            ShaderStage shaderStage
+            ShaderStage shaderStage,
+            bool variableCount = false
         ) noexcept;
         
         uint32_t bindingID;
         DescriptorType descriptorType;
         uint32_t descriptorCount;
         ShaderStage shaderStage;
+        bool variableCount;
     };
 }
diff --git a/include/vkcv/DescriptorWrites.hpp b/include/vkcv/DescriptorWrites.hpp
index 28de2ed7..ff72a911 100644
--- a/include/vkcv/DescriptorWrites.hpp
+++ b/include/vkcv/DescriptorWrites.hpp
@@ -4,12 +4,13 @@
 
 namespace vkcv {
 	struct SampledImageDescriptorWrite {
-		inline SampledImageDescriptorWrite(uint32_t binding, ImageHandle image, uint32_t mipLevel = 0, bool useGeneralLayout = false)
-		    : binding(binding), image(image), mipLevel(mipLevel), useGeneralLayout(useGeneralLayout) {};
+		inline SampledImageDescriptorWrite(uint32_t binding, ImageHandle image, uint32_t mipLevel = 0, bool useGeneralLayout = false, uint32_t arrayIndex = 0)
+		: binding(binding), image(image), mipLevel(mipLevel), useGeneralLayout(useGeneralLayout), arrayIndex(arrayIndex) {};
 		uint32_t	binding;
 		ImageHandle	image;
 		uint32_t    mipLevel;
 		bool        useGeneralLayout;
+		uint32_t    arrayIndex;
 	};
 
 	struct StorageImageDescriptorWrite {
diff --git a/projects/bindless_textures/resources/cube/Grass001_1K_AmbientOcclusion.jpg b/projects/bindless_textures/resources/cube/Grass001_1K_AmbientOcclusion.jpg
new file mode 100644
index 00000000..2217fb53
--- /dev/null
+++ b/projects/bindless_textures/resources/cube/Grass001_1K_AmbientOcclusion.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f6c88f8f3facd9e91f8dd160bd8c4a602e433872ed18e08015a9fa9dfff889de
+size 901465
diff --git a/projects/bindless_textures/resources/cube/Grass001_1K_Color.jpg b/projects/bindless_textures/resources/cube/Grass001_1K_Color.jpg
new file mode 100644
index 00000000..b8aa1533
--- /dev/null
+++ b/projects/bindless_textures/resources/cube/Grass001_1K_Color.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:878b8fe4747d9ce693220edea1915de550e8f14d402d26a0915f162d40f84e91
+size 1763328
diff --git a/projects/bindless_textures/resources/cube/Grass001_1K_Displacement.jpg b/projects/bindless_textures/resources/cube/Grass001_1K_Displacement.jpg
new file mode 100644
index 00000000..89789cba
--- /dev/null
+++ b/projects/bindless_textures/resources/cube/Grass001_1K_Displacement.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1e8300e1107bee7e681059d9da0a7e3ca422977e8b6f496e16452a4c94b3d385
+size 912347
diff --git a/projects/bindless_textures/resources/cube/Grass001_1K_Normal.jpg b/projects/bindless_textures/resources/cube/Grass001_1K_Normal.jpg
new file mode 100644
index 00000000..3163d639
--- /dev/null
+++ b/projects/bindless_textures/resources/cube/Grass001_1K_Normal.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:684426b49e841e267f12f06dc955b5b1d01b3ba3659bea0c5d73be889700929f
+size 2336471
diff --git a/projects/bindless_textures/resources/cube/Grass001_1K_Roughness.jpg b/projects/bindless_textures/resources/cube/Grass001_1K_Roughness.jpg
new file mode 100644
index 00000000..10e6ac33
--- /dev/null
+++ b/projects/bindless_textures/resources/cube/Grass001_1K_Roughness.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d952ffb098faf9ac5eb25134eabac08f0c65d927a448b5e7b4f9c72510cfcbe0
+size 822659
diff --git a/projects/bindless_textures/resources/shaders/shader.frag b/projects/bindless_textures/resources/shaders/shader.frag
index ba9ee901..7b211cb2 100644
--- a/projects/bindless_textures/resources/shaders/shader.frag
+++ b/projects/bindless_textures/resources/shaders/shader.frag
@@ -4,12 +4,13 @@
 
 layout(location = 0) in vec3 passNormal;
 layout(location = 1) in vec2 passUV;
+layout(location = 2) in flat int passTextureIndex;
 
 layout(location = 0) out vec3 outColor;
 
-layout(set=0, binding=0) uniform texture2D  materialTextures[];
-layout(set=0, binding=1) uniform sampler    textureSampler;
+layout(set=0, binding=0) uniform sampler    textureSampler;
+layout(set=0, binding=1) uniform texture2D  materialTextures[];
 
 void main()	{
-	outColor = texture(sampler2D(materialTextures[1], textureSampler), passUV).rgb;
+	outColor = texture(sampler2D(materialTextures[passTextureIndex], textureSampler), passUV).rgb;
 }
\ No newline at end of file
diff --git a/projects/bindless_textures/resources/shaders/shader.vert b/projects/bindless_textures/resources/shaders/shader.vert
index 76855152..57727e4a 100644
--- a/projects/bindless_textures/resources/shaders/shader.vert
+++ b/projects/bindless_textures/resources/shaders/shader.vert
@@ -7,13 +7,33 @@ layout(location = 2) in vec2 inUV;
 
 layout(location = 0) out vec3 passNormal;
 layout(location = 1) out vec2 passUV;
+layout(location = 2) out flat int passTextureIndex;
 
 layout( push_constant ) uniform constants{
     mat4 mvp;
 };
 
-void main()	{
+void main()
+{
 	gl_Position = mvp * vec4(inPosition, 1.0);
 	passNormal  = inNormal;
     passUV      = inUV;
+
+    if(inNormal.x > 0.9)
+        passTextureIndex = 0;
+
+    if(inNormal.x < -0.9)
+        passTextureIndex = 1;
+
+    if(inNormal.y > 0.9)
+        passTextureIndex = 2;
+
+    if(inNormal.y < -0.9)
+        passTextureIndex = 3;
+
+    if(inNormal.z > 0.9)
+        passTextureIndex = 4;
+
+    if(inNormal.z < -0.9)
+        passTextureIndex = 5;
 }
\ No newline at end of file
diff --git a/projects/bindless_textures/src/main.cpp b/projects/bindless_textures/src/main.cpp
index 496c6405..ae5e0306 100644
--- a/projects/bindless_textures/src/main.cpp
+++ b/projects/bindless_textures/src/main.cpp
@@ -30,6 +30,26 @@ int main(int argc, const char** argv) {
 
 	vkcv::asset::Scene mesh;
 
+	// TEST DATA
+	std::vector<vkcv::Image> texturesArray;
+    const std::string grassPaths[5] = { "resources/cube/Grass001_1K_AmbientOcclusion.jpg",
+                                        "resources/cube/Grass001_1K_Color.jpg",
+                                        "resources/cube/Grass001_1K_Displacement.jpg",
+                                        "resources/cube/Grass001_1K_Normal.jpg",
+                                        "resources/cube/Grass001_1K_Roughness.jpg" };
+    for(uint32_t i = 0; i < 5; i++)
+    {
+        std::filesystem::path grassPath(grassPaths[i]);
+        vkcv::asset::TextureData grassTexture = vkcv::asset::loadTexture(grassPath);
+
+        vkcv::Image texture = core.createImage(vk::Format::eR8G8B8A8Srgb, grassTexture.width, grassTexture.height);
+        texture.fill(grassTexture.data.data());
+        texture.generateMipChainImmediate();
+        texture.switchLayout(vk::ImageLayout::eShaderReadOnlyOptimal);
+
+        texturesArray.push_back(texture);
+    }
+
 	const char* path = argc > 1 ? argv[1] : "resources/cube/cube.gltf";
 	int result = vkcv::asset::loadScene(path, mesh);
 
@@ -135,6 +155,7 @@ int main(int argc, const char** argv) {
 	texture.fill(tex.data.data());
 	texture.generateMipChainImmediate();
 	texture.switchLayout(vk::ImageLayout::eShaderReadOnlyOptimal);
+	texturesArray.push_back(texture);
 
 	vkcv::SamplerHandle sampler = core.createSampler(
 		vkcv::SamplerFilterType::LINEAR,
@@ -149,8 +170,18 @@ int main(int argc, const char** argv) {
 		vkcv::VertexBufferBinding(static_cast<vk::DeviceSize>(attributes[2].offset), vertexBuffer.getVulkanHandle()) };
 
 	vkcv::DescriptorWrites setWrites;
-	setWrites.sampledImageWrites	= { vkcv::SampledImageDescriptorWrite(0, texture.getHandle()) };
-	setWrites.samplerWrites			= { vkcv::SamplerDescriptorWrite(1, sampler) };
+	std::vector<vkcv::SampledImageDescriptorWrite> texturesArrayWrites;
+	for(uint32_t i = 0; i < 6; i++)
+	{
+	    texturesArrayWrites.push_back(vkcv::SampledImageDescriptorWrite(1,
+                                                                        texturesArray[i].getHandle(),
+                                                                        0,
+                                                                        false,
+                                                                        i));
+	}
+
+	setWrites.sampledImageWrites	= texturesArrayWrites;
+	setWrites.samplerWrites			= { vkcv::SamplerDescriptorWrite(0, sampler) };
 
 	core.writeDescriptorSet(descriptorSet, setWrites);
 
diff --git a/src/vkcv/Context.cpp b/src/vkcv/Context.cpp
index c143635f..7e9a6b72 100644
--- a/src/vkcv/Context.cpp
+++ b/src/vkcv/Context.cpp
@@ -325,6 +325,7 @@ namespace vkcv
             // NOTE: what about
             // shaderSampledImageArrayNonUniformIndexing ?
             descriptorIndexingFeatures.descriptorBindingPartiallyBound = true;
+            descriptorIndexingFeatures.descriptorBindingVariableDescriptorCount = true;
             descriptorIndexingFeatures.runtimeDescriptorArray = true;
             deviceFeatures2.setPNext(&descriptorIndexingFeatures);
         }
diff --git a/src/vkcv/DescriptorConfig.cpp b/src/vkcv/DescriptorConfig.cpp
index 54e879ac..8d02f889 100644
--- a/src/vkcv/DescriptorConfig.cpp
+++ b/src/vkcv/DescriptorConfig.cpp
@@ -5,11 +5,13 @@ namespace vkcv {
 		uint32_t bindingID,
 		DescriptorType descriptorType,
 		uint32_t descriptorCount,
-		ShaderStage shaderStage) noexcept
+		ShaderStage shaderStage,
+		bool variableCount) noexcept
 		:
 		bindingID(bindingID),
 		descriptorType(descriptorType),
 		descriptorCount(descriptorCount),
-		shaderStage(shaderStage) {}
+		shaderStage(shaderStage),
+		variableCount(variableCount){}
 	
 }
diff --git a/src/vkcv/DescriptorManager.cpp b/src/vkcv/DescriptorManager.cpp
index 0df359a1..8e43259f 100644
--- a/src/vkcv/DescriptorManager.cpp
+++ b/src/vkcv/DescriptorManager.cpp
@@ -48,20 +48,46 @@ namespace vkcv
     {
         std::vector<vk::DescriptorSetLayoutBinding> setBindings = {};
 
-        //create each set's binding
-        for (auto binding : bindings) {
+        // When using a variable descriptor count, the reflected bindings' descriptorCount value is 0.
+        // However, a proper value has to be specified. Problem is, this value still counts towards Vulkan's limits,
+        // which is why we can't really use something like UINT32_MAX. So, 128 has been chosen.
+        const uint32_t variableDescriptorCountLimit = 128;
+
+        //create set's bindings
+        for (auto binding : bindings)
+        {
             vk::DescriptorSetLayoutBinding descriptorSetLayoutBinding(
                 binding.bindingID,
                 convertDescriptorTypeFlag(binding.descriptorType),
                 binding.descriptorCount,
                 convertShaderStageFlag(binding.shaderStage));
+
+            if(binding.variableCount)
+                // magic number
+                descriptorSetLayoutBinding.descriptorCount = variableDescriptorCountLimit;
+
             setBindings.push_back(descriptorSetLayoutBinding);
         }
 
-        DescriptorSet set;
-
-        //create the descriptor set's layout from the bindings gathered above
+        std::vector<vk::DescriptorBindingFlags> bindingFlags;
+        // create binding flags
+        for (auto binding : bindings)
+        {
+            if (binding.variableCount)
+            {
+                bindingFlags.push_back(vk::DescriptorBindingFlagBitsEXT::eVariableDescriptorCount |
+                                       vk::DescriptorBindingFlagBitsEXT::ePartiallyBound);
+            } else
+            {
+                bindingFlags.push_back({});
+            }
+        }
+        vk::DescriptorSetLayoutBindingFlagsCreateInfo bindingFlagsInfo(static_cast<uint32_t>(bindingFlags.size()), bindingFlags.data());
+        //create the descriptor set's layout from the binding and flag information gathered above
         vk::DescriptorSetLayoutCreateInfo layoutInfo({}, setBindings);
+        layoutInfo.setPNext(&bindingFlagsInfo);
+
+        DescriptorSet set;
         if (m_Device.createDescriptorSetLayout(&layoutInfo, nullptr, &set.layout) != vk::Result::eSuccess) {
 			vkcv_log(LogLevel::ERROR, "Failed to create descriptor set layout");
             return DescriptorSetHandle();
@@ -69,6 +95,9 @@ namespace vkcv
         
         //create and allocate the set based on the layout that have been gathered above
         vk::DescriptorSetAllocateInfo allocInfo(m_Pools.back(), 1, &set.layout);
+        vk::DescriptorSetVariableDescriptorCountAllocateInfo variableAllocInfo = {1, &variableDescriptorCountLimit};
+        allocInfo.setPNext(&variableAllocInfo);
+
         auto result = m_Device.allocateDescriptorSets(&allocInfo, &set.vulkanHandle);
         if(result != vk::Result::eSuccess)
         {
@@ -100,6 +129,7 @@ namespace vkcv
 		size_t imageInfoIndex;
 		size_t bufferInfoIndex;
 		uint32_t binding;
+		uint32_t arrayElementIndex;
 		vk::DescriptorType type;
     };
 
@@ -116,7 +146,8 @@ namespace vkcv
 		
 		std::vector<WriteDescriptorSetInfo> writeInfos;
 
-		for (const auto& write : writes.sampledImageWrites) {
+		for (const auto& write : writes.sampledImageWrites)
+		{
 		    vk::ImageLayout layout = write.useGeneralLayout ? vk::ImageLayout::eGeneral : vk::ImageLayout::eShaderReadOnlyOptimal;
 			const vk::DescriptorImageInfo imageInfo(
 				nullptr,
@@ -130,6 +161,7 @@ namespace vkcv
 					imageInfos.size(),
 					0,
 					write.binding,
+					write.arrayIndex,
 					vk::DescriptorType::eSampledImage,
 			};
 			
@@ -149,6 +181,7 @@ namespace vkcv
 					imageInfos.size(),
 					0,
 					write.binding,
+					0,
 					vk::DescriptorType::eStorageImage
 			};
 			
@@ -173,6 +206,7 @@ namespace vkcv
 					0,
 					bufferInfos.size(),
 					write.binding,
+					0,
 					write.dynamic?
 					vk::DescriptorType::eUniformBufferDynamic :
 					vk::DescriptorType::eUniformBuffer
@@ -199,6 +233,7 @@ namespace vkcv
 					0,
 					bufferInfos.size(),
 					write.binding,
+					0,
 					write.dynamic?
 					vk::DescriptorType::eStorageBufferDynamic :
 					vk::DescriptorType::eStorageBuffer
@@ -222,6 +257,7 @@ namespace vkcv
 					imageInfos.size(),
 					0,
 					write.binding,
+					0,
 					vk::DescriptorType::eSampler
 			};
 			
@@ -234,7 +270,7 @@ namespace vkcv
 			vk::WriteDescriptorSet vulkanWrite(
 					set,
 					write.binding,
-					static_cast<uint32_t>(0),
+					write.arrayElementIndex,
 					1,
 					write.type,
 					(write.imageInfoIndex > 0? &(imageInfos[write.imageInfoIndex - 1]) : nullptr),
diff --git a/src/vkcv/ShaderProgram.cpp b/src/vkcv/ShaderProgram.cpp
index 971797d9..1308a1b2 100644
--- a/src/vkcv/ShaderProgram.cpp
+++ b/src/vkcv/ShaderProgram.cpp
@@ -175,12 +175,33 @@ namespace vkcv {
         for (uint32_t i = 0; i < resources.separate_images.size(); i++) {
             auto& u = resources.separate_images[i];
             const spirv_cross::SPIRType& base_type = comp.get_type(u.base_type_id);
-            std::pair descriptor(comp.get_decoration(u.id, spv::DecorationDescriptorSet),
-                DescriptorBinding(comp.get_decoration(u.id, spv::DecorationBinding), DescriptorType::IMAGE_SAMPLED, base_type.vecsize, shaderStage));
+
+            // we require the type (not base type!) to query array information
+            const spirv_cross::SPIRType& type      = comp.get_type(u.type_id);
+
+            uint32_t descriptorCount = 1;
+            bool variableCount = false;
+
+            if(type.array_size_literal[0])
+            {
+                if(type.array[0] == 0)
+                    variableCount = true;
+
+                descriptorCount = type.array[0];
+            }
+
+            DescriptorBinding descBinding(comp.get_decoration(u.id, spv::DecorationBinding),
+                                          DescriptorType::IMAGE_SAMPLED,
+                                          descriptorCount,
+                                          shaderStage,
+                                          variableCount);
+
+            std::pair<uint32_t, DescriptorBinding> descriptor(comp.get_decoration(u.id, spv::DecorationDescriptorSet), descBinding);
+
+
             bindings.push_back(descriptor);
             if ((int32_t)comp.get_decoration(u.id, spv::DecorationDescriptorSet) > maxSetID)
                 maxSetID = comp.get_decoration(u.id, spv::DecorationDescriptorSet);
-
         }
 
         for (uint32_t i = 0; i < resources.storage_images.size(); i++) {
-- 
GitLab