55*
66* Relevant code parts are marked with [POI]
77*
8- * Copyright (C) 2018-2023 by Sascha Willems - www.saschawillems.de
8+ * Copyright (C) 2018-2025 by Sascha Willems - www.saschawillems.de
99*
1010* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
1111*/
@@ -27,7 +27,7 @@ class VulkanExample : public VulkanExampleBase
2727 float r, g, b;
2828 float ambient;
2929 } material;
30- VkDescriptorSet descriptorSet ;
30+ std::vector< VkDescriptorSet> descriptorSets ;
3131 void setRandomMaterial (bool applyRandomSeed) {
3232 std::random_device rndDevice;
3333 std::default_random_engine rndEngine (applyRandomSeed ? rndDevice () : 0 );
@@ -48,26 +48,31 @@ class VulkanExample : public VulkanExampleBase
4848 glm::mat4 view;
4949 glm::vec3 camPos;
5050 } uniformData;
51- vks::Buffer uniformBuffer ;
51+ std::vector< vks::Buffer> uniformBuffers ;
5252
5353 VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE };
5454 VkPipeline pipeline{ VK_NULL_HANDLE };
55- VkDescriptorSet descriptorSet{ VK_NULL_HANDLE };
5655
5756 struct DescriptorSetLaysts {
5857 VkDescriptorSetLayout scene{ VK_NULL_HANDLE };
5958 VkDescriptorSetLayout object{ VK_NULL_HANDLE };
6059 } descriptorSetLayouts;
60+ std::vector<VkDescriptorSet> descriptorSets;
61+
62+ bool doUpdateMaterials{ false };
6163
6264 VulkanExample () : VulkanExampleBase()
6365 {
66+ useNewSync = true ;
6467 title = " Inline uniform blocks" ;
6568 camera.type = Camera::CameraType::firstperson;
6669 camera.setPosition (glm::vec3 (0 .0f , 0 .0f , -10 .0f ));
6770 camera.setRotation (glm::vec3 (0.0 , 0 .0f , 0 .0f ));
6871 camera.setPerspective (60 .0f , (float )width / (float )height, 0 .1f , 256 .0f );
6972 camera.movementSpeed = 4 .0f ;
7073 camera.rotationSpeed = 0 .25f ;
74+ uniformBuffers.resize (maxConcurrentFrames);
75+ descriptorSets.resize (maxConcurrentFrames);
7176
7277 /*
7378 [POI] Enable extensions required for inline uniform blocks
@@ -91,67 +96,9 @@ class VulkanExample : public VulkanExampleBase
9196 vkDestroyPipelineLayout (device, pipelineLayout, nullptr );
9297 vkDestroyDescriptorSetLayout (device, descriptorSetLayouts.scene , nullptr );
9398 vkDestroyDescriptorSetLayout (device, descriptorSetLayouts.object , nullptr );
94- uniformBuffer.destroy ();
95- }
96- }
97-
98- void buildCommandBuffers ()
99- {
100- VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo ();
101-
102- VkClearValue clearValues[2 ];
103- clearValues[0 ].color = { { 0 .15f , 0 .15f , 0 .15f , 1 .0f } };
104- clearValues[1 ].depthStencil = { 1 .0f , 0 };
105-
106- VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo ();
107- renderPassBeginInfo.renderPass = renderPass;
108- renderPassBeginInfo.renderArea .offset .x = 0 ;
109- renderPassBeginInfo.renderArea .offset .y = 0 ;
110- renderPassBeginInfo.renderArea .extent .width = width;
111- renderPassBeginInfo.renderArea .extent .height = height;
112- renderPassBeginInfo.clearValueCount = 2 ;
113- renderPassBeginInfo.pClearValues = clearValues;
114-
115- for (int32_t i = 0 ; i < drawCmdBuffers.size (); ++i)
116- {
117- renderPassBeginInfo.framebuffer = frameBuffers[i];
118-
119- VK_CHECK_RESULT (vkBeginCommandBuffer (drawCmdBuffers[i], &cmdBufInfo));
120-
121- vkCmdBeginRenderPass (drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
122-
123- VkViewport viewport = vks::initializers::viewport ((float )width, (float )height, 0 .0f , 1 .0f );
124- vkCmdSetViewport (drawCmdBuffers[i], 0 , 1 , &viewport);
125-
126- VkRect2D scissor = vks::initializers::rect2D (width, height, 0 , 0 );
127- vkCmdSetScissor (drawCmdBuffers[i], 0 , 1 , &scissor);
128-
129- // Render objects
130- vkCmdBindPipeline (drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
131-
132- uint32_t objcount = static_cast <uint32_t >(objects.size ());
133- for (uint32_t x = 0 ; x < objcount; x++) {
134- /*
135- [POI] Bind descriptor sets
136- Set 0 = Scene matrices:
137- Set 1 = Object inline uniform block (In shader pbr.frag: layout (set = 1, binding = 0) uniform UniformInline ... )
138- */
139- std::vector<VkDescriptorSet> descriptorSets = {
140- descriptorSet,
141- objects[x].descriptorSet
142- };
143- vkCmdBindDescriptorSets (drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0 , 2 , descriptorSets.data (), 0 , nullptr );
144-
145- glm::vec3 pos = glm::vec3 (sin (glm::radians (x * (360 .0f / objcount))), cos (glm::radians (x * (360 .0f / objcount))), 0 .0f ) * 3 .5f ;
146-
147- vkCmdPushConstants (drawCmdBuffers[i], pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0 , sizeof (glm::vec3), &pos);
148- model.draw (drawCmdBuffers[i]);
99+ for (auto & buffer : uniformBuffers) {
100+ buffer.destroy ();
149101 }
150- drawUI (drawCmdBuffers[i]);
151-
152- vkCmdEndRenderPass (drawCmdBuffers[i]);
153-
154- VK_CHECK_RESULT (vkEndCommandBuffer (drawCmdBuffers[i]));
155102 }
156103 }
157104
@@ -169,11 +116,11 @@ class VulkanExample : public VulkanExampleBase
169116 {
170117 // Pool
171118 std::vector<VkDescriptorPoolSize> poolSizes = {
172- vks::initializers::descriptorPoolSize (VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1 ),
119+ vks::initializers::descriptorPoolSize (VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, maxConcurrentFrames ),
173120 /* [POI] Allocate inline uniform blocks */
174121 vks::initializers::descriptorPoolSize (VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT, static_cast <uint32_t >(objects.size ()) * sizeof (Object::Material)),
175122 };
176- VkDescriptorPoolCreateInfo descriptorPoolCI = vks::initializers::descriptorPoolCreateInfo (poolSizes, static_cast <uint32_t >(objects.size ()) + 1 );
123+ VkDescriptorPoolCreateInfo descriptorPoolCI = vks::initializers::descriptorPoolCreateInfo (poolSizes, ( static_cast <uint32_t >(objects.size ()) + 1 ) * maxConcurrentFrames );
177124 /*
178125 [POI] New structure that has to be chained into the descriptor pool's createinfo if you want to allocate inline uniform blocks
179126 */
@@ -203,43 +150,45 @@ class VulkanExample : public VulkanExampleBase
203150 descriptorLayoutCI = vks::initializers::descriptorSetLayoutCreateInfo (setLayoutBindings);
204151 VK_CHECK_RESULT (vkCreateDescriptorSetLayout (device, &descriptorLayoutCI, nullptr , &descriptorSetLayouts.object ));
205152
206- // Sets
207- // Scene
208- VkDescriptorSetAllocateInfo descriptorAllocateInfo = vks::initializers::descriptorSetAllocateInfo (descriptorPool, &descriptorSetLayouts.scene , 1 );
209- VK_CHECK_RESULT (vkAllocateDescriptorSets (device, &descriptorAllocateInfo, &descriptorSet));
210- std::vector<VkWriteDescriptorSet> writeDescriptorSets = {
211- vks::initializers::writeDescriptorSet (descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0 , &uniformBuffer.descriptor ),
212- };
213- vkUpdateDescriptorSets (device, static_cast <uint32_t >(writeDescriptorSets.size ()), writeDescriptorSets.data (), 0 , nullptr );
214- // Objects
215- for (auto & object : objects) {
216- VkDescriptorSetAllocateInfo descriptorAllocateInfo = vks::initializers::descriptorSetAllocateInfo (descriptorPool, &descriptorSetLayouts.object , 1 );
217- VK_CHECK_RESULT (vkAllocateDescriptorSets (device, &descriptorAllocateInfo, &object.descriptorSet ));
153+ // Sets per frame, just like the buffers themselves
154+ for (auto i = 0 ; i < uniformBuffers.size (); i++) {
155+ VkDescriptorSetAllocateInfo descriptorAllocateInfo = vks::initializers::descriptorSetAllocateInfo (descriptorPool, &descriptorSetLayouts.scene , 1 );
156+ VK_CHECK_RESULT (vkAllocateDescriptorSets (device, &descriptorAllocateInfo, &descriptorSets[i]));
157+ std::vector<VkWriteDescriptorSet> writeDescriptorSets = {
158+ vks::initializers::writeDescriptorSet (descriptorSets[i], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0 , &uniformBuffers[i].descriptor ),
159+ };
160+ vkUpdateDescriptorSets (device, static_cast <uint32_t >(writeDescriptorSets.size ()), writeDescriptorSets.data (), 0 , nullptr );
161+ // Objects with inline uniform blocks
162+ for (auto & object : objects) {
163+ object.descriptorSets .resize (maxConcurrentFrames);
164+ VkDescriptorSetAllocateInfo descriptorAllocateInfo = vks::initializers::descriptorSetAllocateInfo (descriptorPool, &descriptorSetLayouts.object , 1 );
165+ VK_CHECK_RESULT (vkAllocateDescriptorSets (device, &descriptorAllocateInfo, &object.descriptorSets [i]));
218166
219- /*
220- [POI] New structure that defines size and data of the inline uniform block needs to be chained into the write descriptor set
221- We will be using this inline uniform block to pass per-object material information to the fragment shader
222- */
223- VkWriteDescriptorSetInlineUniformBlockEXT writeDescriptorSetInlineUniformBlock{};
224- writeDescriptorSetInlineUniformBlock.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK_EXT;
225- writeDescriptorSetInlineUniformBlock.dataSize = sizeof (Object::Material);
226- // Uniform data for the inline block
227- writeDescriptorSetInlineUniformBlock.pData = &object.material ;
228-
229- /*
230- [POI] Setup the inline uniform block
231- */
232- VkWriteDescriptorSet writeDescriptorSet{};
233- writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
234- writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT;
235- writeDescriptorSet.dstSet = object.descriptorSet ;
236- writeDescriptorSet.dstBinding = 0 ;
237- // Descriptor count for an inline uniform block contains data sizes of the block(last parameter)
238- writeDescriptorSet.descriptorCount = sizeof (Object::Material);
239- // Chain inline uniform block structure
240- writeDescriptorSet.pNext = &writeDescriptorSetInlineUniformBlock;
167+ /*
168+ [POI] New structure that defines size and data of the inline uniform block needs to be chained into the write descriptor set
169+ We will be using this inline uniform block to pass per-object material information to the fragment shader
170+ */
171+ VkWriteDescriptorSetInlineUniformBlockEXT writeDescriptorSetInlineUniformBlock{};
172+ writeDescriptorSetInlineUniformBlock.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK_EXT;
173+ writeDescriptorSetInlineUniformBlock.dataSize = sizeof (Object::Material);
174+ // Uniform data for the inline block
175+ writeDescriptorSetInlineUniformBlock.pData = &object.material ;
241176
242- vkUpdateDescriptorSets (device, 1 , &writeDescriptorSet, 0 , nullptr );
177+ /*
178+ [POI] Setup the inline uniform block
179+ */
180+ VkWriteDescriptorSet writeDescriptorSet{};
181+ writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
182+ writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT;
183+ writeDescriptorSet.dstSet = object.descriptorSets [i];
184+ writeDescriptorSet.dstBinding = 0 ;
185+ // Descriptor count for an inline uniform block contains data sizes of the block(last parameter)
186+ writeDescriptorSet.descriptorCount = sizeof (Object::Material);
187+ // Chain inline uniform block structure
188+ writeDescriptorSet.pNext = &writeDescriptorSetInlineUniformBlock;
189+
190+ vkUpdateDescriptorSets (device, 1 , &writeDescriptorSet, 0 , nullptr );
191+ }
243192 }
244193 }
245194
@@ -273,7 +222,7 @@ class VulkanExample : public VulkanExampleBase
273222 VkPipelineMultisampleStateCreateInfo multisampleStateCI = vks::initializers::pipelineMultisampleStateCreateInfo (VK_SAMPLE_COUNT_1_BIT);
274223 std::vector<VkDynamicState> dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
275224 VkPipelineDynamicStateCreateInfo dynamicStateCI = vks::initializers::pipelineDynamicStateCreateInfo (dynamicStateEnables);
276- std::array<VkPipelineShaderStageCreateInfo, 2 > shaderStages;
225+ std::array<VkPipelineShaderStageCreateInfo, 2 > shaderStages{} ;
277226
278227 VkGraphicsPipelineCreateInfo pipelineCI = vks::initializers::pipelineCreateInfo (pipelineLayout, renderPass);
279228 pipelineCI.pInputAssemblyState = &inputAssemblyStateCI;
@@ -294,9 +243,10 @@ class VulkanExample : public VulkanExampleBase
294243
295244 void prepareUniformBuffers ()
296245 {
297- VK_CHECK_RESULT (vulkanDevice->createBuffer (VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &uniformBuffer, sizeof (UniformData)));
298- VK_CHECK_RESULT (uniformBuffer.map ());
299- updateUniformBuffers ();
246+ for (auto & buffer : uniformBuffers) {
247+ VK_CHECK_RESULT (vulkanDevice->createBuffer (VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &buffer, sizeof (UniformData), &uniformData));
248+ VK_CHECK_RESULT (buffer.map ());
249+ }
300250 }
301251
302252 void updateUniformBuffers ()
@@ -305,47 +255,14 @@ class VulkanExample : public VulkanExampleBase
305255 uniformData.view = camera.matrices .view ;
306256 uniformData.model = glm::scale (glm::mat4 (1 .0f ), glm::vec3 (0 .5f ));
307257 uniformData.camPos = camera.position * glm::vec3 (-1 .0f , 1 .0f , -1 .0f );
308- memcpy (uniformBuffer.mapped , &uniformData, sizeof (UniformData));
309- }
310-
311- void prepare ()
312- {
313- VulkanExampleBase::prepare ();
314- loadAssets ();
315- prepareUniformBuffers ();
316- setupDescriptors ();
317- preparePipelines ();
318- buildCommandBuffers ();
319- prepared = true ;
320- }
321-
322- void draw ()
323- {
324- VulkanExampleBase::prepareFrame ();
325- submitInfo.commandBufferCount = 1 ;
326- submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
327- VK_CHECK_RESULT (vkQueueSubmit (queue, 1 , &submitInfo, VK_NULL_HANDLE));
328- VulkanExampleBase::submitFrame ();
329- }
330-
331- virtual void render ()
332- {
333- if (!prepared)
334- return ;
335- updateUniformBuffers ();
336- draw ();
258+ memcpy (uniformBuffers[currentBuffer].mapped , &uniformData, sizeof (UniformData));
337259 }
338260
339261 /*
340- [POI] Update descriptor sets at runtime, called from the UI to randomize materials
262+ [POI] Update descriptor set data at runtime using inline uniform blocks
341263 */
342264 void updateMaterials () {
343- // Setup random materials for every object in the scene
344- for (uint32_t i = 0 ; i < objects.size (); i++) {
345- objects[i].setRandomMaterial (!benchmark.active );
346- }
347-
348- for (auto &object : objects) {
265+ for (auto & object : objects) {
349266 /*
350267 [POI] New structure that defines size and data of the inline uniform block needs to be chained into the write descriptor set
351268 We will be using this inline uniform block to pass per-object material information to the fragment shader
@@ -362,7 +279,7 @@ class VulkanExample : public VulkanExampleBase
362279 VkWriteDescriptorSet writeDescriptorSet{};
363280 writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
364281 writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT;
365- writeDescriptorSet.dstSet = object.descriptorSet ;
282+ writeDescriptorSet.dstSet = object.descriptorSets [currentBuffer] ;
366283 writeDescriptorSet.dstBinding = 0 ;
367284 writeDescriptorSet.descriptorCount = sizeof (Object::Material);
368285 writeDescriptorSet.pNext = &writeDescriptorSetInlineUniformBlock;
@@ -371,10 +288,93 @@ class VulkanExample : public VulkanExampleBase
371288 }
372289 }
373290
291+ void prepare ()
292+ {
293+ VulkanExampleBase::prepare ();
294+ loadAssets ();
295+ prepareUniformBuffers ();
296+ setupDescriptors ();
297+ preparePipelines ();
298+ prepared = true ;
299+ }
300+
301+ void buildCommandBuffer ()
302+ {
303+ VkCommandBuffer cmdBuffer = drawCmdBuffers[currentBuffer];
304+ vkResetCommandBuffer (cmdBuffer, 0 );
305+
306+ VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo ();
307+
308+ VkClearValue clearValues[2 ]{};
309+ clearValues[0 ].color = { { 0 .15f , 0 .15f , 0 .15f , 1 .0f } };
310+ clearValues[1 ].depthStencil = { 1 .0f , 0 };
311+
312+ VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo ();
313+ renderPassBeginInfo.renderPass = renderPass;
314+ renderPassBeginInfo.renderArea .offset .x = 0 ;
315+ renderPassBeginInfo.renderArea .offset .y = 0 ;
316+ renderPassBeginInfo.renderArea .extent .width = width;
317+ renderPassBeginInfo.renderArea .extent .height = height;
318+ renderPassBeginInfo.clearValueCount = 2 ;
319+ renderPassBeginInfo.pClearValues = clearValues;
320+ renderPassBeginInfo.framebuffer = frameBuffers[currentImageIndex];
321+
322+ VK_CHECK_RESULT (vkBeginCommandBuffer (cmdBuffer, &cmdBufInfo));
323+
324+ vkCmdBeginRenderPass (cmdBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
325+
326+ VkViewport viewport = vks::initializers::viewport ((float )width, (float )height, 0 .0f , 1 .0f );
327+ vkCmdSetViewport (cmdBuffer, 0 , 1 , &viewport);
328+
329+ VkRect2D scissor = vks::initializers::rect2D (width, height, 0 , 0 );
330+ vkCmdSetScissor (cmdBuffer, 0 , 1 , &scissor);
331+
332+ // Render objects
333+ vkCmdBindPipeline (cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
334+
335+ uint32_t objcount = static_cast <uint32_t >(objects.size ());
336+ for (uint32_t i = 0 ; i < objcount; i++) {
337+ /*
338+ [POI] Bind descriptor sets
339+ Set 0 = Scene matrices:
340+ Set 1 = Object inline uniform block (In shader pbr.frag: layout (set = 1, binding = 0) uniform UniformInline ... )
341+ */
342+ std::vector<VkDescriptorSet> sets = {
343+ descriptorSets[currentBuffer],
344+ objects[i].descriptorSets [currentBuffer]
345+ };
346+ vkCmdBindDescriptorSets (cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0 , 2 , sets.data (), 0 , nullptr );
347+
348+ glm::vec3 pos = glm::vec3 (sin (glm::radians (i * (360 .0f / objcount))), cos (glm::radians (i * (360 .0f / objcount))), 0 .0f ) * 3 .5f ;
349+
350+ vkCmdPushConstants (cmdBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0 , sizeof (glm::vec3), &pos);
351+ model.draw (cmdBuffer);
352+ }
353+ drawUI (cmdBuffer);
354+
355+ vkCmdEndRenderPass (cmdBuffer);
356+
357+ VK_CHECK_RESULT (vkEndCommandBuffer (cmdBuffer));
358+ }
359+
360+ virtual void render ()
361+ {
362+ if (!prepared)
363+ return ;
364+ VulkanExampleBase::prepareFrame ();
365+ updateUniformBuffers ();
366+ updateMaterials ();
367+ buildCommandBuffer ();
368+ VulkanExampleBase::submitFrame ();
369+ }
370+
374371 virtual void OnUpdateUIOverlay (vks::UIOverlay *overlay)
375372 {
376373 if (overlay->button (" Randomize" )) {
377- updateMaterials ();
374+ // Randomize material properties
375+ for (uint32_t i = 0 ; i < objects.size (); i++) {
376+ objects[i].setRandomMaterial (!benchmark.active );
377+ }
378378 }
379379 }
380380
0 commit comments