@@ -117,7 +117,8 @@ class HelloTriangleApplication {
117117 vk::Extent2D swapChainExtent;
118118 std::vector<vk::raii::ImageView> swapChainImageViews;
119119
120- vk::raii::DescriptorSetLayout descriptorSetLayout = nullptr ;
120+ vk::raii::DescriptorSetLayout descriptorSetLayoutGlobal = nullptr ;
121+ vk::raii::DescriptorSetLayout descriptorSetLayoutMaterial = nullptr ;
121122 vk::raii::PipelineLayout pipelineLayout = nullptr ;
122123 vk::raii::Pipeline graphicsPipeline = nullptr ;
123124
@@ -153,7 +154,8 @@ class HelloTriangleApplication {
153154 std::vector<tinyobj::material_t > materials;
154155
155156 vk::raii::DescriptorPool descriptorPool = nullptr ;
156- std::vector<vk::raii::DescriptorSet> descriptorSets;
157+ std::vector<vk::raii::DescriptorSet> globalDescriptorSets;
158+ std::vector<vk::raii::DescriptorSet> materialDescriptorSets;
157159
158160 vk::raii::CommandPool commandPool = nullptr ;
159161 std::vector<vk::raii::CommandBuffer> commandBuffers;
@@ -197,12 +199,12 @@ class HelloTriangleApplication {
197199 createLogicalDevice ();
198200 createSwapChain ();
199201 createImageViews ();
202+ createCommandPool ();
203+ loadModel ();
200204 createDescriptorSetLayout ();
201205 createGraphicsPipeline ();
202- createCommandPool ();
203206 createDepthResources ();
204207 createTextureSampler ();
205- loadModel ();
206208 createVertexBuffer ();
207209 createIndexBuffer ();
208210 createUniformBuffers ();
@@ -343,10 +345,18 @@ class HelloTriangleApplication {
343345 { return strcmp ( availableDeviceExtension.extensionName , requiredDeviceExtension ) == 0 ; } );
344346 } );
345347
346- auto features = device.template getFeatures2 <vk::PhysicalDeviceFeatures2, vk::PhysicalDeviceVulkan13Features, vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT>();
348+ auto features = device.template getFeatures2 <vk::PhysicalDeviceFeatures2,
349+ vk::PhysicalDeviceVulkan12Features,
350+ vk::PhysicalDeviceVulkan13Features,
351+ vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT>();
347352 bool supportsRequiredFeatures = features.template get <vk::PhysicalDeviceFeatures2>().features .samplerAnisotropy &&
348353 features.template get <vk::PhysicalDeviceVulkan13Features>().dynamicRendering &&
349- features.template get <vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT>().extendedDynamicState ;
354+ features.template get <vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT>().extendedDynamicState &&
355+ features.template get <vk::PhysicalDeviceVulkan12Features>().descriptorBindingSampledImageUpdateAfterBind &&
356+ features.template get <vk::PhysicalDeviceVulkan12Features>().descriptorBindingPartiallyBound &&
357+ features.template get <vk::PhysicalDeviceVulkan12Features>().descriptorBindingVariableDescriptorCount &&
358+ features.template get <vk::PhysicalDeviceVulkan12Features>().runtimeDescriptorArray &&
359+ features.template get <vk::PhysicalDeviceVulkan12Features>().shaderSampledImageArrayNonUniformIndexing ;
350360
351361 return supportsVulkan1_3 && supportsGraphics && supportsAllRequiredExtensions && supportsRequiredFeatures;
352362 } );
@@ -409,10 +419,14 @@ class HelloTriangleApplication {
409419 }
410420
411421 // query for Vulkan 1.3 features
412- vk::StructureChain<vk::PhysicalDeviceFeatures2, vk::PhysicalDeviceVulkan13Features, vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT> featureChain = {
413- {.features = {.samplerAnisotropy = true } }, // vk::PhysicalDeviceFeatures2
414- {.synchronization2 = true , .dynamicRendering = true }, // vk::PhysicalDeviceVulkan13Features
415- {.extendedDynamicState = true } // vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT
422+ vk::StructureChain<vk::PhysicalDeviceFeatures2, vk::PhysicalDeviceVulkan12Features,
423+ vk::PhysicalDeviceVulkan13Features, vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT> featureChain = {
424+ {.features = {.samplerAnisotropy = true } }, // vk::PhysicalDeviceFeatures2
425+ {.shaderSampledImageArrayNonUniformIndexing = true , .descriptorBindingSampledImageUpdateAfterBind = true ,
426+ .descriptorBindingPartiallyBound = true , .descriptorBindingVariableDescriptorCount = true ,
427+ .runtimeDescriptorArray = true }, // vk::PhysicalDeviceVulkan12Features
428+ {.synchronization2 = true , .dynamicRendering = true }, // vk::PhysicalDeviceVulkan13Features
429+ {.extendedDynamicState = true } // vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT
416430 };
417431
418432 // create a Device
@@ -462,13 +476,41 @@ class HelloTriangleApplication {
462476 }
463477
464478 void createDescriptorSetLayout () {
465- std::array bindings = {
466- vk::DescriptorSetLayoutBinding ( 0 , vk::DescriptorType::eUniformBuffer, 1 , vk::ShaderStageFlagBits::eVertex, nullptr ),
467- vk::DescriptorSetLayoutBinding ( 1 , vk::DescriptorType::eCombinedImageSampler, 1 , vk::ShaderStageFlagBits::eFragment, nullptr )
479+ // Use descriptor set 0 for global data
480+ std::array global_bindings = {
481+ vk::DescriptorSetLayoutBinding ( 0 , vk::DescriptorType::eUniformBuffer, 1 , vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment, nullptr ),
482+ };
483+
484+ vk::DescriptorSetLayoutCreateInfo globalLayoutInfo{ .bindingCount = static_cast <uint32_t >(global_bindings.size ()), .pBindings = global_bindings.data () };
485+
486+ descriptorSetLayoutGlobal = vk::raii::DescriptorSetLayout (device, globalLayoutInfo);
487+
488+ // Use descriptor set 1 for bindless material data
489+ uint32_t textureCount = static_cast <uint32_t >(textureImageViews.size ());
490+
491+ std::array material_bindings = {
492+ vk::DescriptorSetLayoutBinding ( 0 , vk::DescriptorType::eSampler, 1 , vk::ShaderStageFlagBits::eFragment, nullptr ),
493+ vk::DescriptorSetLayoutBinding ( 1 , vk::DescriptorType::eSampledImage, static_cast <uint32_t >(textureCount), vk::ShaderStageFlagBits::eFragment, nullptr )
468494 };
469495
470- vk::DescriptorSetLayoutCreateInfo layoutInfo{ .bindingCount = static_cast <uint32_t >(bindings.size ()), .pBindings = bindings.data () };
471- descriptorSetLayout = vk::raii::DescriptorSetLayout (device, layoutInfo);
496+ std::vector<vk::DescriptorBindingFlags> bindingFlags = {
497+ vk::DescriptorBindingFlagBits::eUpdateAfterBind,
498+ vk::DescriptorBindingFlagBits::ePartiallyBound | vk::DescriptorBindingFlagBits::eVariableDescriptorCount | vk::DescriptorBindingFlagBits::eUpdateAfterBind
499+ };
500+
501+ vk::DescriptorSetLayoutBindingFlagsCreateInfo flagsCreateInfo{
502+ .bindingCount = static_cast <uint32_t >(bindingFlags.size ()),
503+ .pBindingFlags = bindingFlags.data ()
504+ };
505+
506+ vk::DescriptorSetLayoutCreateInfo materialLayoutInfo{
507+ .pNext = &flagsCreateInfo,
508+ .flags = vk::DescriptorSetLayoutCreateFlagBits::eUpdateAfterBindPool,
509+ .bindingCount = static_cast <uint32_t >(material_bindings.size ()),
510+ .pBindings = material_bindings.data (),
511+ };
512+
513+ descriptorSetLayoutMaterial = vk::raii::DescriptorSetLayout (device, materialLayoutInfo);
472514 }
473515
474516 void createGraphicsPipeline () {
@@ -531,13 +573,15 @@ class HelloTriangleApplication {
531573 };
532574 vk::PipelineDynamicStateCreateInfo dynamicState{ .dynamicStateCount = static_cast <uint32_t >(dynamicStates.size ()), .pDynamicStates = dynamicStates.data () };
533575
576+ vk::DescriptorSetLayout setLayouts[] = {*descriptorSetLayoutGlobal, *descriptorSetLayoutMaterial};
577+
534578 vk::PushConstantRange pushConstantRange {
535579 .stageFlags = vk::ShaderStageFlagBits::eFragment,
536580 .offset = 0 ,
537581 .size = sizeof (PushConstant)
538582 };
539583
540- vk::PipelineLayoutCreateInfo pipelineLayoutInfo{ .setLayoutCount = 1 , .pSetLayouts = &*descriptorSetLayout , .pushConstantRangeCount = 1 , .pPushConstantRanges = &pushConstantRange };
584+ vk::PipelineLayoutCreateInfo pipelineLayoutInfo{ .setLayoutCount = 2 , .pSetLayouts = setLayouts , .pushConstantRangeCount = 1 , .pPushConstantRanges = &pushConstantRange };
541585
542586 pipelineLayout = vk::raii::PipelineLayout (device, pipelineLayoutInfo);
543587
@@ -885,59 +929,109 @@ class HelloTriangleApplication {
885929 void createDescriptorPool () {
886930 std::array poolSize {
887931 vk::DescriptorPoolSize ( vk::DescriptorType::eUniformBuffer, MAX_FRAMES_IN_FLIGHT),
888- vk::DescriptorPoolSize ( vk::DescriptorType::eCombinedImageSampler, MAX_FRAMES_IN_FLIGHT)
932+ vk::DescriptorPoolSize ( vk::DescriptorType::eSampler, MAX_FRAMES_IN_FLIGHT),
933+ vk::DescriptorPoolSize ( vk::DescriptorType::eSampledImage, (uint32_t )materials.size ())
889934 };
890935 vk::DescriptorPoolCreateInfo poolInfo{
891- .flags = vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet,
892- .maxSets = MAX_FRAMES_IN_FLIGHT,
936+ .flags = vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet |
937+ vk::DescriptorPoolCreateFlagBits::eUpdateAfterBind,
938+ .maxSets = MAX_FRAMES_IN_FLIGHT + 1 , // + 1 for bindless materials
893939 .poolSizeCount = static_cast <uint32_t >(poolSize.size ()),
894940 .pPoolSizes = poolSize.data ()
895941 };
896942 descriptorPool = vk::raii::DescriptorPool (device, poolInfo);
897943 }
898944
899945 void createDescriptorSets () {
900- std::vector<vk::DescriptorSetLayout> layouts (MAX_FRAMES_IN_FLIGHT, descriptorSetLayout);
901- vk::DescriptorSetAllocateInfo allocInfo{
946+ // Global descriptor sets (per frame)
947+ std::vector<vk::DescriptorSetLayout> globalLayouts (MAX_FRAMES_IN_FLIGHT, descriptorSetLayoutGlobal);
948+
949+ vk::DescriptorSetAllocateInfo allocInfoGlobal{
902950 .descriptorPool = descriptorPool,
903- .descriptorSetCount = static_cast <uint32_t >(layouts .size ()),
904- .pSetLayouts = layouts .data ()
951+ .descriptorSetCount = static_cast <uint32_t >(globalLayouts .size ()),
952+ .pSetLayouts = globalLayouts .data ()
905953 };
906954
907- descriptorSets .clear ();
908- descriptorSets = device.allocateDescriptorSets (allocInfo );
955+ globalDescriptorSets .clear ();
956+ globalDescriptorSets = device.allocateDescriptorSets (allocInfoGlobal );
909957
910958 for (size_t i = 0 ; i < MAX_FRAMES_IN_FLIGHT; i++) {
959+ // Uniform buffer
911960 vk::DescriptorBufferInfo bufferInfo{
912961 .buffer = uniformBuffers[i],
913962 .offset = 0 ,
914963 .range = sizeof (UniformBufferObject)
915964 };
965+
966+ vk::WriteDescriptorSet bufferWrite{
967+ .dstSet = globalDescriptorSets[i],
968+ .dstBinding = 0 ,
969+ .dstArrayElement = 0 ,
970+ .descriptorCount = 1 ,
971+ .descriptorType = vk::DescriptorType::eUniformBuffer,
972+ .pBufferInfo = &bufferInfo
973+ };
974+
975+ std::array<vk::WriteDescriptorSet, 1 > descriptorWrites{bufferWrite};
976+
977+ device.updateDescriptorSets (descriptorWrites, {});
978+ }
979+
980+ // Material descriptor sets (per material)
981+ std::vector<uint32_t > variableCounts = { static_cast <uint32_t >(textureImageViews.size ()) };
982+ vk::DescriptorSetVariableDescriptorCountAllocateInfo variableCountInfo{
983+ .descriptorSetCount = 1 ,
984+ .pDescriptorCounts = variableCounts.data ()
985+ };
986+
987+ std::vector<vk::DescriptorSetLayout> layouts{ *descriptorSetLayoutMaterial };
988+
989+ vk::DescriptorSetAllocateInfo allocInfo {
990+ .pNext = &variableCountInfo,
991+ .descriptorPool = descriptorPool,
992+ .descriptorSetCount = static_cast <uint32_t >(layouts.size ()),
993+ .pSetLayouts = layouts.data ()
994+ };
995+
996+ materialDescriptorSets = device.allocateDescriptorSets (allocInfo);
997+
998+ // Sampler
999+ vk::DescriptorImageInfo samplerInfo{
1000+ .sampler = textureSampler
1001+ };
1002+
1003+ vk::WriteDescriptorSet samplerWrite{
1004+ .dstSet = materialDescriptorSets[0 ],
1005+ .dstBinding = 0 ,
1006+ .dstArrayElement = 0 ,
1007+ .descriptorCount = 1 ,
1008+ .descriptorType = vk::DescriptorType::eSampler,
1009+ .pImageInfo = &samplerInfo
1010+ };
1011+
1012+ device.updateDescriptorSets ({samplerWrite}, {});
1013+
1014+ // Textures
1015+ std::vector<vk::DescriptorImageInfo> imageInfos;
1016+ imageInfos.reserve (textureImageViews.size ());
1017+ for (auto & iv : textureImageViews) {
9161018 vk::DescriptorImageInfo imageInfo{
917- .sampler = textureSampler,
918- .imageView = textureImageViews[0 ],
1019+ .imageView = iv,
9191020 .imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal
9201021 };
921- std::array descriptorWrites{
922- vk::WriteDescriptorSet{
923- .dstSet = descriptorSets[i],
924- .dstBinding = 0 ,
925- .dstArrayElement = 0 ,
926- .descriptorCount = 1 ,
927- .descriptorType = vk::DescriptorType::eUniformBuffer,
928- .pBufferInfo = &bufferInfo
929- },
930- vk::WriteDescriptorSet{
931- .dstSet = descriptorSets[i],
932- .dstBinding = 1 ,
933- .dstArrayElement = 0 ,
934- .descriptorCount = 1 ,
935- .descriptorType = vk::DescriptorType::eCombinedImageSampler,
936- .pImageInfo = &imageInfo
937- }
938- };
939- device.updateDescriptorSets (descriptorWrites, {});
1022+ imageInfos.push_back (imageInfo);
9401023 }
1024+
1025+ vk::WriteDescriptorSet materialWrite{
1026+ .dstSet = materialDescriptorSets[0 ],
1027+ .dstBinding = 1 ,
1028+ .dstArrayElement = 0 ,
1029+ .descriptorCount = static_cast <uint32_t >(imageInfos.size ()),
1030+ .descriptorType = vk::DescriptorType::eSampledImage,
1031+ .pImageInfo = imageInfos.data ()
1032+ };
1033+
1034+ device.updateDescriptorSets ({materialWrite}, {});
9411035 }
9421036
9431037 void createBuffer (vk::DeviceSize size, vk::BufferUsageFlags usage, vk::MemoryPropertyFlags properties, vk::raii::Buffer& buffer, vk::raii::DeviceMemory& bufferMemory) {
@@ -1079,7 +1173,8 @@ class HelloTriangleApplication {
10791173 commandBuffers[currentFrame].setScissor (0 , vk::Rect2D (vk::Offset2D (0 , 0 ), swapChainExtent));
10801174 commandBuffers[currentFrame].bindVertexBuffers (0 , *vertexBuffer, {0 });
10811175 commandBuffers[currentFrame].bindIndexBuffer ( *indexBuffer, 0 , vk::IndexType::eUint32 );
1082- commandBuffers[currentFrame].bindDescriptorSets (vk::PipelineBindPoint::eGraphics, pipelineLayout, 0 , *descriptorSets[currentFrame], nullptr );
1176+ commandBuffers[currentFrame].bindDescriptorSets (vk::PipelineBindPoint::eGraphics, pipelineLayout, 0 , *globalDescriptorSets[currentFrame], nullptr );
1177+ commandBuffers[currentFrame].bindDescriptorSets (vk::PipelineBindPoint::eGraphics, pipelineLayout, 1 , *materialDescriptorSets[0 ], nullptr );
10831178
10841179 for (auto & sub : submeshes) {
10851180 uint32_t idx = sub.materialID < 0 ? 0u : static_cast <uint32_t >(sub.materialID );
0 commit comments