11/*
22* Vulkan Example - Dynamic uniform buffers
33*
4- * Copyright (C) 2016-2023 by Sascha Willems - www.saschawillems.de
4+ * Copyright (C) 2016-2025 by Sascha Willems - www.saschawillems.de
55*
66* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
77*
1818
1919#include " vulkanexamplebase.h"
2020
21- # define OBJECT_INSTANCES 125
21+ constexpr auto OBJECT_INSTANCES = 125 ;
2222
2323// Vertex layout for this example
2424struct Vertex {
@@ -57,10 +57,11 @@ class VulkanExample : public VulkanExampleBase
5757 vks::Buffer indexBuffer;
5858 uint32_t indexCount{ 0 };
5959
60- struct {
60+ struct UniformBuffers {
6161 vks::Buffer view;
6262 vks::Buffer dynamic;
63- } uniformBuffers;
63+ };
64+ std::array<UniformBuffers, maxConcurrentFrames> uniformBuffers;
6465
6566 struct {
6667 glm::mat4 projection;
@@ -82,12 +83,11 @@ class VulkanExample : public VulkanExampleBase
8283 VkDescriptorSet descriptorSet{ VK_NULL_HANDLE };
8384 VkDescriptorSetLayout descriptorSetLayout{ VK_NULL_HANDLE };
8485
85- float animationTimer{ 0 .0f };
86-
8786 size_t dynamicAlignment{ 0 };
8887
8988 VulkanExample () : VulkanExampleBase()
9089 {
90+ useNewSync = true ;
9191 title = " Dynamic uniform buffers" ;
9292 camera.type = Camera::CameraType::lookat;
9393 camera.setPosition (glm::vec3 (0 .0f , 0 .0f , -30 .0f ));
@@ -106,64 +106,10 @@ class VulkanExample : public VulkanExampleBase
106106 vkDestroyDescriptorSetLayout (device, descriptorSetLayout, nullptr );
107107 vertexBuffer.destroy ();
108108 indexBuffer.destroy ();
109- uniformBuffers.view .destroy ();
110- uniformBuffers.dynamic .destroy ();
111- }
112- }
113-
114- void buildCommandBuffers ()
115- {
116- VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo ();
117-
118- VkClearValue clearValues[2 ];
119- clearValues[0 ].color = defaultClearColor;
120- clearValues[1 ].depthStencil = { 1 .0f , 0 };
121-
122- VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo ();
123- renderPassBeginInfo.renderPass = renderPass;
124- renderPassBeginInfo.renderArea .offset .x = 0 ;
125- renderPassBeginInfo.renderArea .offset .y = 0 ;
126- renderPassBeginInfo.renderArea .extent .width = width;
127- renderPassBeginInfo.renderArea .extent .height = height;
128- renderPassBeginInfo.clearValueCount = 2 ;
129- renderPassBeginInfo.pClearValues = clearValues;
130-
131- for (int32_t i = 0 ; i < drawCmdBuffers.size (); ++i)
132- {
133- renderPassBeginInfo.framebuffer = frameBuffers[i];
134-
135- VK_CHECK_RESULT (vkBeginCommandBuffer (drawCmdBuffers[i], &cmdBufInfo));
136-
137- vkCmdBeginRenderPass (drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
138-
139- VkViewport viewport = vks::initializers::viewport ((float )width, (float )height, 0 .0f , 1 .0f );
140- vkCmdSetViewport (drawCmdBuffers[i], 0 , 1 , &viewport);
141-
142- VkRect2D scissor = vks::initializers::rect2D (width, height, 0 , 0 );
143- vkCmdSetScissor (drawCmdBuffers[i], 0 , 1 , &scissor);
144-
145- vkCmdBindPipeline (drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
146-
147- VkDeviceSize offsets[1 ] = { 0 };
148- vkCmdBindVertexBuffers (drawCmdBuffers[i], 0 , 1 , &vertexBuffer.buffer , offsets);
149- vkCmdBindIndexBuffer (drawCmdBuffers[i], indexBuffer.buffer , 0 , VK_INDEX_TYPE_UINT32);
150-
151- // Render multiple objects using different model matrices by dynamically offsetting into one uniform buffer
152- for (uint32_t j = 0 ; j < OBJECT_INSTANCES; j++)
153- {
154- // One dynamic offset per dynamic descriptor to offset into the ubo containing all model matrices
155- uint32_t dynamicOffset = j * static_cast <uint32_t >(dynamicAlignment);
156- // Bind the descriptor set for rendering a mesh using the dynamic offset
157- vkCmdBindDescriptorSets (drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0 , 1 , &descriptorSet, 1 , &dynamicOffset);
158-
159- vkCmdDrawIndexed (drawCmdBuffers[i], indexCount, 1 , 0 , 0 , 0 );
109+ for (auto & buffer : uniformBuffers) {
110+ buffer.view .destroy ();
111+ buffer.dynamic .destroy ();
160112 }
161-
162- drawUI (drawCmdBuffers[i]);
163-
164- vkCmdEndRenderPass (drawCmdBuffers[i]);
165-
166- VK_CHECK_RESULT (vkEndCommandBuffer (drawCmdBuffers[i]));
167113 }
168114 }
169115
@@ -208,12 +154,12 @@ class VulkanExample : public VulkanExampleBase
208154 {
209155 // Pool
210156 std::vector<VkDescriptorPoolSize> poolSizes = {
211- vks::initializers::descriptorPoolSize (VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1 ),
212- // Dynamic uniform buffer
213- vks::initializers::descriptorPoolSize (VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1 )
157+ vks::initializers::descriptorPoolSize (VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, maxConcurrentFrames ),
158+ // Dynamic uniform buffers require a different descriptor type
159+ vks::initializers::descriptorPoolSize (VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, maxConcurrentFrames )
214160 };
215161
216- VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo (poolSizes, 2 );
162+ VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo (poolSizes, maxConcurrentFrames );
217163 VK_CHECK_RESULT (vkCreateDescriptorPool (device, &descriptorPoolInfo, nullptr , &descriptorPool));
218164
219165 // Layout
@@ -226,17 +172,20 @@ class VulkanExample : public VulkanExampleBase
226172 VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo (setLayoutBindings);
227173 VK_CHECK_RESULT (vkCreateDescriptorSetLayout (device, &descriptorLayout, nullptr , &descriptorSetLayout));
228174
229- // Set
175+ // Sets per frame, just like the buffers themselves
176+ // Images do not need to be duplicated per frame, we reuse the same one for each frame
230177 VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo (descriptorPool, &descriptorSetLayout, 1 );
231- VK_CHECK_RESULT (vkAllocateDescriptorSets (device, &allocInfo, &descriptorSet));
232-
233- std::vector<VkWriteDescriptorSet> writeDescriptorSets = {
234- // Binding 0 : Projection/View matrix as uniform buffer
235- vks::initializers::writeDescriptorSet (descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0 , &uniformBuffers.view .descriptor ),
236- // Binding 1 : Instance matrix as dynamic uniform buffer
237- vks::initializers::writeDescriptorSet (descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1 , &uniformBuffers.dynamic .descriptor ),
238- };
239- vkUpdateDescriptorSets (device, static_cast <uint32_t >(writeDescriptorSets.size ()), writeDescriptorSets.data (), 0 , nullptr );
178+ for (auto i = 0 ; i < uniformBuffers.size (); i++) {
179+ VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo (descriptorPool, &descriptorSetLayout, 1 );
180+ VK_CHECK_RESULT (vkAllocateDescriptorSets (device, &allocInfo, &descriptorSet));
181+ std::vector<VkWriteDescriptorSet> writeDescriptorSets = {
182+ // Binding 0 : Projection/View matrix as uniform buffer
183+ vks::initializers::writeDescriptorSet (descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0 , &uniformBuffers[currentBuffer].view .descriptor ),
184+ // Binding 1 : Instance matrix as dynamic uniform buffer
185+ vks::initializers::writeDescriptorSet (descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1 , &uniformBuffers[currentBuffer].dynamic .descriptor ),
186+ };
187+ vkUpdateDescriptorSets (device, static_cast <uint32_t >(writeDescriptorSets.size ()), writeDescriptorSets.data (), 0 , nullptr );
188+ }
240189 }
241190
242191 void preparePipelines ()
@@ -255,7 +204,7 @@ class VulkanExample : public VulkanExampleBase
255204 VkPipelineMultisampleStateCreateInfo multisampleState = vks::initializers::pipelineMultisampleStateCreateInfo (VK_SAMPLE_COUNT_1_BIT, 0 );
256205 std::vector<VkDynamicState> dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
257206 VkPipelineDynamicStateCreateInfo dynamicState = vks::initializers::pipelineDynamicStateCreateInfo (dynamicStateEnables);
258- std::array<VkPipelineShaderStageCreateInfo, 2 > shaderStages;
207+ std::array<VkPipelineShaderStageCreateInfo, 2 > shaderStages{} ;
259208
260209 // Vertex bindings and attributes
261210 VkVertexInputBindingDescription vertexInputBinding = {
@@ -309,28 +258,28 @@ class VulkanExample : public VulkanExampleBase
309258 std::cout << " minUniformBufferOffsetAlignment = " << minUboAlignment << std::endl;
310259 std::cout << " dynamicAlignment = " << dynamicAlignment << std::endl;
311260
312- // Vertex shader uniform buffer block
313-
314- // Static shared uniform buffer object with projection and view matrix
315- VK_CHECK_RESULT (vulkanDevice-> createBuffer (
316- VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT ,
317- VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ,
318- &uniformBuffers. view ,
319- sizeof (uboVS)));
320-
321- // Uniform buffer object with per-object matrices
322- VK_CHECK_RESULT (vulkanDevice-> createBuffer (
323- VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT ,
324- VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT ,
325- &uniformBuffers. dynamic ,
326- bufferSize));
327-
328- // Override descriptor range to [base, base + dynamicAlignment]
329- uniformBuffers. dynamic . descriptor . range = dynamicAlignment;
330-
331- // Map persistent
332- VK_CHECK_RESULT (uniformBuffers. view .map ());
333- VK_CHECK_RESULT (uniformBuffers. dynamic . map ());
261+ for ( auto & buffer : uniformBuffers) {
262+ // Static shared uniform buffer object with projection and view matrix
263+ VK_CHECK_RESULT (vulkanDevice-> createBuffer (
264+ VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
265+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ,
266+ &buffer. view ,
267+ sizeof (uboVS)));
268+
269+ // Uniform buffer object with per-object matrices
270+ VK_CHECK_RESULT (vulkanDevice-> createBuffer (
271+ VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
272+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT ,
273+ &buffer. dynamic ,
274+ bufferSize));
275+
276+ // Override descriptor range to [base, base + dynamicAlignment]
277+ buffer. dynamic . descriptor . range = dynamicAlignment;
278+
279+ // Map persistent
280+ VK_CHECK_RESULT (buffer. view . map ());
281+ VK_CHECK_RESULT (buffer. dynamic .map ());
282+ }
334283
335284 // Prepare per-object matrices with offsets and random rotations
336285 std::default_random_engine rndEngine (benchmark.active ? 0 : (unsigned )time (nullptr ));
@@ -339,45 +288,31 @@ class VulkanExample : public VulkanExampleBase
339288 rotations[i] = glm::vec3 (rndDist (rndEngine), rndDist (rndEngine), rndDist (rndEngine)) * 2 .0f * (float )M_PI;
340289 rotationSpeeds[i] = glm::vec3 (rndDist (rndEngine), rndDist (rndEngine), rndDist (rndEngine));
341290 }
342-
343- updateUniformBuffers ();
344- updateDynamicUniformBuffer ();
345291 }
346292
347293 void updateUniformBuffers ()
348294 {
349295 // Fixed ubo with projection and view matrices
350296 uboVS.projection = camera.matrices .perspective ;
351297 uboVS.view = camera.matrices .view ;
352-
353- memcpy (uniformBuffers.view .mapped , &uboVS, sizeof (uboVS));
298+ memcpy (uniformBuffers[currentBuffer].view .mapped , &uboVS, sizeof (uboVS));
354299 }
355300
356301 void updateDynamicUniformBuffer ()
357302 {
358- // Update at max. 60 fps
359- animationTimer += frameTimer;
360- if (animationTimer <= 1 .0f / 60 .0f ) {
361- return ;
362- }
363-
364303 // Dynamic ubo with per-object model matrices indexed by offsets in the command buffer
365304 uint32_t dim = static_cast <uint32_t >(pow (OBJECT_INSTANCES, (1 .0f / 3 .0f )));
366305 glm::vec3 offset (5 .0f );
367-
368- for (uint32_t x = 0 ; x < dim; x++)
369- {
370- for (uint32_t y = 0 ; y < dim; y++)
371- {
372- for (uint32_t z = 0 ; z < dim; z++)
373- {
374- uint32_t index = x * dim * dim + y * dim + z;
306+ for (uint32_t x = 0 ; x < dim; x++) {
307+ for (uint32_t y = 0 ; y < dim; y++) {
308+ for (uint32_t z = 0 ; z < dim; z++) {
309+ const uint32_t index = x * dim * dim + y * dim + z;
375310
376311 // Aligned offset
377312 glm::mat4* modelMat = (glm::mat4*)(((uint64_t )uboDataDynamic.model + (index * dynamicAlignment)));
378313
379314 // Update rotations
380- rotations[index] += animationTimer * rotationSpeeds[index];
315+ rotations[index] += frameTimer * rotationSpeeds[index];
381316
382317 // Update matrices
383318 glm::vec3 pos = glm::vec3 (-((dim * offset.x ) / 2 .0f ) + offset.x / 2 .0f + x * offset.x , -((dim * offset.y ) / 2 .0f ) + offset.y / 2 .0f + y * offset.y , -((dim * offset.z ) / 2 .0f ) + offset.z / 2 .0f + z * offset.z );
@@ -388,14 +323,11 @@ class VulkanExample : public VulkanExampleBase
388323 }
389324 }
390325 }
391-
392- animationTimer = 0 .0f ;
393-
394- memcpy (uniformBuffers.dynamic .mapped , uboDataDynamic.model , uniformBuffers.dynamic .size );
326+ memcpy (uniformBuffers[currentBuffer].dynamic .mapped , uboDataDynamic.model , uniformBuffers[currentBuffer].dynamic .size );
395327 // Flush to make changes visible to the host
396328 VkMappedMemoryRange memoryRange = vks::initializers::mappedMemoryRange ();
397- memoryRange.memory = uniformBuffers.dynamic .memory ;
398- memoryRange.size = uniformBuffers.dynamic .size ;
329+ memoryRange.memory = uniformBuffers[currentBuffer] .dynamic .memory ;
330+ memoryRange.size = uniformBuffers[currentBuffer] .dynamic .size ;
399331 vkFlushMappedMemoryRanges (device, 1 , &memoryRange);
400332 }
401333
@@ -406,26 +338,72 @@ class VulkanExample : public VulkanExampleBase
406338 prepareUniformBuffers ();
407339 setupDescriptors ();
408340 preparePipelines ();
409- buildCommandBuffers ();
410341 prepared = true ;
411342 }
412343
413- void draw ()
344+ void buildCommandBuffer ()
414345 {
415- VulkanExampleBase::prepareFrame ();
416- submitInfo.commandBufferCount = 1 ;
417- submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
418- VK_CHECK_RESULT (vkQueueSubmit (queue, 1 , &submitInfo, VK_NULL_HANDLE));
419- VulkanExampleBase::submitFrame ();
346+ VkCommandBuffer cmdBuffer = drawCmdBuffers[currentBuffer];
347+ vkResetCommandBuffer (cmdBuffer, 0 );
348+
349+ VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo ();
350+
351+ VkClearValue clearValues[2 ]{};
352+ clearValues[0 ].color = defaultClearColor;
353+ clearValues[1 ].depthStencil = { 1 .0f , 0 };
354+
355+ VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo ();
356+ renderPassBeginInfo.renderPass = renderPass;
357+ renderPassBeginInfo.renderArea .offset .x = 0 ;
358+ renderPassBeginInfo.renderArea .offset .y = 0 ;
359+ renderPassBeginInfo.renderArea .extent .width = width;
360+ renderPassBeginInfo.renderArea .extent .height = height;
361+ renderPassBeginInfo.clearValueCount = 2 ;
362+ renderPassBeginInfo.pClearValues = clearValues;
363+ renderPassBeginInfo.framebuffer = frameBuffers[currentImageIndex];
364+
365+ VK_CHECK_RESULT (vkBeginCommandBuffer (cmdBuffer, &cmdBufInfo));
366+
367+ vkCmdBeginRenderPass (cmdBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
368+
369+ VkViewport viewport = vks::initializers::viewport ((float )width, (float )height, 0 .0f , 1 .0f );
370+ vkCmdSetViewport (cmdBuffer, 0 , 1 , &viewport);
371+
372+ VkRect2D scissor = vks::initializers::rect2D (width, height, 0 , 0 );
373+ vkCmdSetScissor (cmdBuffer, 0 , 1 , &scissor);
374+
375+ vkCmdBindPipeline (cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
376+
377+ VkDeviceSize offsets[1 ] = { 0 };
378+ vkCmdBindVertexBuffers (cmdBuffer, 0 , 1 , &vertexBuffer.buffer , offsets);
379+ vkCmdBindIndexBuffer (cmdBuffer, indexBuffer.buffer , 0 , VK_INDEX_TYPE_UINT32);
380+
381+ // Render multiple objects using different model matrices by dynamically offsetting into one uniform buffer
382+ for (uint32_t j = 0 ; j < OBJECT_INSTANCES; j++) {
383+ // One dynamic offset per dynamic descriptor to offset into the ubo containing all model matrices
384+ uint32_t dynamicOffset = j * static_cast <uint32_t >(dynamicAlignment);
385+ // Bind the descriptor set for rendering a mesh using the dynamic offset
386+ vkCmdBindDescriptorSets (cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0 , 1 , &descriptorSet, 1 , &dynamicOffset);
387+
388+ vkCmdDrawIndexed (cmdBuffer, indexCount, 1 , 0 , 0 , 0 );
389+ }
390+
391+ drawUI (cmdBuffer);
392+
393+ vkCmdEndRenderPass (cmdBuffer);
394+
395+ VK_CHECK_RESULT (vkEndCommandBuffer (cmdBuffer));
420396 }
421397
422398 virtual void render ()
423399 {
424400 if (!prepared)
425401 return ;
402+ VulkanExampleBase::prepareFrame ();
426403 updateUniformBuffers ();
427404 updateDynamicUniformBuffer ();
428- draw ();
405+ buildCommandBuffer ();
406+ VulkanExampleBase::submitFrame ();
429407 }
430408};
431409
0 commit comments