11/*
22* Vulkan Example - glTF scene loading and rendering
33*
4- * Copyright (C) 2020-2023 by Sascha Willems - www.saschawillems.de
4+ * Copyright (C) 2020-2025 by Sascha Willems - www.saschawillems.de
55*
66* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
77*/
@@ -384,37 +384,37 @@ class VulkanExample : public VulkanExampleBase
384384
385385 VulkanglTFModel glTFModel;
386386
387- struct ShaderData {
388- vks::Buffer buffer;
389- struct Values {
390- glm::mat4 projection;
391- glm::mat4 model;
392- glm::vec4 lightPos = glm::vec4(5 .0f , 5 .0f , -5 .0f , 1 .0f );
393- glm::vec4 viewPos;
394- } values;
395- } shaderData;
387+ struct UniformData {
388+ glm::mat4 projection;
389+ glm::mat4 model;
390+ glm::vec4 lightPos = glm::vec4(5 .0f , 5 .0f , -5 .0f , 1 .0f );
391+ glm::vec4 viewPos;
392+ } uniformData;
393+ std::vector<vks::Buffer> uniformBuffers;
396394
395+ VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE };
397396 struct Pipelines {
398397 VkPipeline solid{ VK_NULL_HANDLE };
399398 VkPipeline wireframe{ VK_NULL_HANDLE };
400399 } pipelines;
401400
402- VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE };
403- VkDescriptorSet descriptorSet{ VK_NULL_HANDLE };
404-
405401 struct DescriptorSetLayouts {
406402 VkDescriptorSetLayout matrices{ VK_NULL_HANDLE };
407403 VkDescriptorSetLayout textures{ VK_NULL_HANDLE };
408404 } descriptorSetLayouts;
405+ std::vector<VkDescriptorSet> descriptorSets;
409406
410407 VulkanExample () : VulkanExampleBase()
411408 {
409+ useNewSync = true ;
412410 title = " glTF model rendering" ;
413411 camera.type = Camera::CameraType::lookat;
414412 camera.flipY = true ;
415413 camera.setPosition (glm::vec3 (0 .0f , -0 .1f , -1 .0f ));
416414 camera.setRotation (glm::vec3 (0 .0f , 45 .0f , 0 .0f ));
417415 camera.setPerspective (60 .0f , (float )width / (float )height, 0 .1f , 256 .0f );
416+ uniformBuffers.resize (maxConcurrentFrames);
417+ descriptorSets.resize (maxConcurrentFrames);
418418 }
419419
420420 ~VulkanExample ()
@@ -427,7 +427,9 @@ class VulkanExample : public VulkanExampleBase
427427 vkDestroyPipelineLayout (device, pipelineLayout, nullptr );
428428 vkDestroyDescriptorSetLayout (device, descriptorSetLayouts.matrices , nullptr );
429429 vkDestroyDescriptorSetLayout (device, descriptorSetLayouts.textures , nullptr );
430- shaderData.buffer .destroy ();
430+ for (auto & buffer : uniformBuffers) {
431+ buffer.destroy ();
432+ }
431433 }
432434 }
433435
@@ -439,43 +441,6 @@ class VulkanExample : public VulkanExampleBase
439441 };
440442 }
441443
442- void buildCommandBuffers ()
443- {
444- VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo ();
445-
446- VkClearValue clearValues[2 ];
447- clearValues[0 ].color = { { 0 .25f , 0 .25f , 0 .25f , 1 .0f } };;
448- clearValues[1 ].depthStencil = { 1 .0f , 0 };
449-
450- VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo ();
451- renderPassBeginInfo.renderPass = renderPass;
452- renderPassBeginInfo.renderArea .offset .x = 0 ;
453- renderPassBeginInfo.renderArea .offset .y = 0 ;
454- renderPassBeginInfo.renderArea .extent .width = width;
455- renderPassBeginInfo.renderArea .extent .height = height;
456- renderPassBeginInfo.clearValueCount = 2 ;
457- renderPassBeginInfo.pClearValues = clearValues;
458-
459- const VkViewport viewport = vks::initializers::viewport ((float )width, (float )height, 0 .0f , 1 .0f );
460- const VkRect2D scissor = vks::initializers::rect2D (width, height, 0 , 0 );
461-
462- for (int32_t i = 0 ; i < drawCmdBuffers.size (); ++i)
463- {
464- renderPassBeginInfo.framebuffer = frameBuffers[i];
465- VK_CHECK_RESULT (vkBeginCommandBuffer (drawCmdBuffers[i], &cmdBufInfo));
466- vkCmdBeginRenderPass (drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
467- vkCmdSetViewport (drawCmdBuffers[i], 0 , 1 , &viewport);
468- vkCmdSetScissor (drawCmdBuffers[i], 0 , 1 , &scissor);
469- // Bind scene matrices descriptor to set 0
470- vkCmdBindDescriptorSets (drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0 , 1 , &descriptorSet, 0 , nullptr );
471- vkCmdBindPipeline (drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, wireframe ? pipelines.wireframe : pipelines.solid );
472- glTFModel.draw (drawCmdBuffers[i], pipelineLayout);
473- drawUI (drawCmdBuffers[i]);
474- vkCmdEndRenderPass (drawCmdBuffers[i]);
475- VK_CHECK_RESULT (vkEndCommandBuffer (drawCmdBuffers[i]));
476- }
477- }
478-
479444 void loadglTFFile (std::string filename)
480445 {
481446 tinygltf::Model glTFInput;
@@ -521,26 +486,21 @@ class VulkanExample : public VulkanExampleBase
521486 size_t indexBufferSize = indexBuffer.size () * sizeof (uint32_t );
522487 glTFModel.indices .count = static_cast <uint32_t >(indexBuffer.size ());
523488
524- struct StagingBuffer {
525- VkBuffer buffer;
526- VkDeviceMemory memory;
527- } vertexStaging, indexStaging;
489+ vks::Buffer vertexStaging, indexStaging;
528490
529491 // Create host visible staging buffers (source)
530492 VK_CHECK_RESULT (vulkanDevice->createBuffer (
531493 VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
532494 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
495+ &vertexStaging,
533496 vertexBufferSize,
534- &vertexStaging.buffer ,
535- &vertexStaging.memory ,
536497 vertexBuffer.data ()));
537498 // Index data
538499 VK_CHECK_RESULT (vulkanDevice->createBuffer (
539500 VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
540501 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
502+ &indexStaging,
541503 indexBufferSize,
542- &indexStaging.buffer ,
543- &indexStaging.memory ,
544504 indexBuffer.data ()));
545505
546506 // Create device local buffers (target)
@@ -579,11 +539,8 @@ class VulkanExample : public VulkanExampleBase
579539
580540 vulkanDevice->flushCommandBuffer (copyCmd, queue, true );
581541
582- // Free staging resources
583- vkDestroyBuffer (device, vertexStaging.buffer , nullptr );
584- vkFreeMemory (device, vertexStaging.memory , nullptr );
585- vkDestroyBuffer (device, indexStaging.buffer , nullptr );
586- vkFreeMemory (device, indexStaging.memory , nullptr );
542+ vertexStaging.destroy ();
543+ indexStaging.destroy ();
587544 }
588545
589546 void loadAssets ()
@@ -598,12 +555,12 @@ class VulkanExample : public VulkanExampleBase
598555 */
599556
600557 std::vector<VkDescriptorPoolSize> poolSizes = {
601- vks::initializers::descriptorPoolSize (VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1 ),
558+ vks::initializers::descriptorPoolSize (VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, maxConcurrentFrames ),
602559 // One combined image sampler per model image/texture
603- vks::initializers::descriptorPoolSize (VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, static_cast <uint32_t >(glTFModel.images .size ())),
560+ vks::initializers::descriptorPoolSize (VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, static_cast <uint32_t >(glTFModel.images .size ()) * maxConcurrentFrames ),
604561 };
605562 // One set for matrices and one per model image/texture
606- const uint32_t maxSetCount = static_cast <uint32_t >(glTFModel.images .size ()) + 1 ;
563+ const uint32_t maxSetCount = ( static_cast <uint32_t >(glTFModel.images .size ()) + 1 ) * maxConcurrentFrames ;
607564 VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo (poolSizes, maxSetCount);
608565 VK_CHECK_RESULT (vkCreateDescriptorPool (device, &descriptorPoolInfo, nullptr , &descriptorPool));
609566
@@ -615,12 +572,15 @@ class VulkanExample : public VulkanExampleBase
615572 setLayoutBinding = vks::initializers::descriptorSetLayoutBinding (VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 0 );
616573 VK_CHECK_RESULT (vkCreateDescriptorSetLayout (device, &descriptorSetLayoutCI, nullptr , &descriptorSetLayouts.textures ));
617574
618- // Descriptor set for scene matrices
619- VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo (descriptorPool, &descriptorSetLayouts.matrices , 1 );
620- VK_CHECK_RESULT (vkAllocateDescriptorSets (device, &allocInfo, &descriptorSet));
621- VkWriteDescriptorSet writeDescriptorSet = vks::initializers::writeDescriptorSet (descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0 , &shaderData.buffer .descriptor );
622- vkUpdateDescriptorSets (device, 1 , &writeDescriptorSet, 0 , nullptr );
623- // Descriptor sets for materials
575+ // Descriptor set for scene matrices per frame, just like the buffers themselves
576+ for (auto i = 0 ; i < uniformBuffers.size (); i++) {
577+ VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo (descriptorPool, &descriptorSetLayouts.matrices , 1 );
578+ VK_CHECK_RESULT (vkAllocateDescriptorSets (device, &allocInfo, &descriptorSets[i]));
579+ VkWriteDescriptorSet writeDescriptorSet = vks::initializers::writeDescriptorSet (descriptorSets[i], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0 , &uniformBuffers[i].descriptor );
580+ vkUpdateDescriptorSets (device, 1 , &writeDescriptorSet, 0 , nullptr );
581+ }
582+
583+ // Descriptor sets for materials, since they only use static images, no need to duplicate them per frame
624584 for (auto & image : glTFModel.images ) {
625585 const VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo (descriptorPool, &descriptorSetLayouts.textures , 1 );
626586 VK_CHECK_RESULT (vkAllocateDescriptorSets (device, &allocInfo, &image.descriptorSet ));
@@ -699,18 +659,18 @@ class VulkanExample : public VulkanExampleBase
699659 // Prepare and initialize uniform buffer containing shader uniforms
700660 void prepareUniformBuffers ()
701661 {
702- // Vertex shader uniform buffer block
703- VK_CHECK_RESULT (vulkanDevice->createBuffer (VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &shaderData. buffer , sizeof (shaderData. values ) ));
704- // Map persistent
705- VK_CHECK_RESULT (shaderData. buffer . map ());
662+ for ( auto & buffer : uniformBuffers) {
663+ 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 ));
664+ VK_CHECK_RESULT (buffer. map ());
665+ }
706666 }
707667
708668 void updateUniformBuffers ()
709669 {
710- shaderData. values .projection = camera.matrices .perspective ;
711- shaderData. values .model = camera.matrices .view ;
712- shaderData. values .viewPos = camera.viewPos ;
713- memcpy (shaderData. buffer . mapped , &shaderData. values , sizeof (shaderData. values ));
670+ uniformData .projection = camera.matrices .perspective ;
671+ uniformData .model = camera.matrices .view ;
672+ uniformData .viewPos = camera.viewPos ;
673+ memcpy (uniformBuffers[currentBuffer]. mapped , &uniformData , sizeof (UniformData ));
714674 }
715675
716676 void prepare ()
@@ -720,23 +680,59 @@ class VulkanExample : public VulkanExampleBase
720680 prepareUniformBuffers ();
721681 setupDescriptors ();
722682 preparePipelines ();
723- buildCommandBuffers ();
724683 prepared = true ;
725684 }
726685
686+ void buildCommandBuffer ()
687+ {
688+ VkCommandBuffer cmdBuffer = drawCmdBuffers[currentBuffer];
689+ vkResetCommandBuffer (cmdBuffer, 0 );
690+
691+ VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo ();
692+
693+ VkClearValue clearValues[2 ]{};
694+ clearValues[0 ].color = { { 0 .25f , 0 .25f , 0 .25f , 1 .0f } };;
695+ clearValues[1 ].depthStencil = { 1 .0f , 0 };
696+
697+ VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo ();
698+ renderPassBeginInfo.renderPass = renderPass;
699+ renderPassBeginInfo.renderArea .offset .x = 0 ;
700+ renderPassBeginInfo.renderArea .offset .y = 0 ;
701+ renderPassBeginInfo.renderArea .extent .width = width;
702+ renderPassBeginInfo.renderArea .extent .height = height;
703+ renderPassBeginInfo.clearValueCount = 2 ;
704+ renderPassBeginInfo.pClearValues = clearValues;
705+ renderPassBeginInfo.framebuffer = frameBuffers[currentImageIndex];
706+
707+ VK_CHECK_RESULT (vkBeginCommandBuffer (cmdBuffer, &cmdBufInfo));
708+ vkCmdBeginRenderPass (cmdBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
709+ const VkViewport viewport = vks::initializers::viewport ((float )width, (float )height, 0 .0f , 1 .0f );
710+ vkCmdSetViewport (cmdBuffer, 0 , 1 , &viewport);
711+ const VkRect2D scissor = vks::initializers::rect2D (width, height, 0 , 0 );
712+ vkCmdSetScissor (cmdBuffer, 0 , 1 , &scissor);
713+ // Bind scene matrices descriptor to set 0
714+ vkCmdBindDescriptorSets (cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0 , 1 , &descriptorSets[currentBuffer], 0 , nullptr );
715+ vkCmdBindPipeline (cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, wireframe ? pipelines.wireframe : pipelines.solid );
716+ glTFModel.draw (cmdBuffer, pipelineLayout);
717+ drawUI (cmdBuffer);
718+ vkCmdEndRenderPass (cmdBuffer);
719+ VK_CHECK_RESULT (vkEndCommandBuffer (cmdBuffer));
720+ }
721+
727722 virtual void render ()
728723 {
724+ if (!prepared)
725+ return ;
726+ VulkanExampleBase::prepareFrame ();
729727 updateUniformBuffers ();
730- renderFrame ();
731-
728+ buildCommandBuffer ();
729+ VulkanExampleBase::submitFrame ();
732730 }
733731
734732 virtual void OnUpdateUIOverlay (vks::UIOverlay *overlay)
735733 {
736734 if (overlay->header (" Settings" )) {
737- if (overlay->checkBox (" Wireframe" , &wireframe)) {
738- buildCommandBuffers ();
739- }
735+ overlay->checkBox (" Wireframe" , &wireframe);
740736 }
741737 }
742738};
0 commit comments