From 5bc82e9f029f08c4ef41633869432f86c7c020ff Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Thu, 21 Apr 2022 07:34:44 +0200 Subject: [PATCH] Updated graphics pipeline library sample --- .../glsl/graphicspipelinelibrary/shared.vert | 33 + .../graphicspipelinelibrary/shared.vert.spv | Bin 0 -> 2548 bytes .../glsl/graphicspipelinelibrary/uber.frag | 61 ++ .../graphicspipelinelibrary/uber.frag.spv | Bin 0 -> 3984 bytes .../graphicspipelinelibrary.cpp | 580 ++++++++---------- 5 files changed, 356 insertions(+), 318 deletions(-) create mode 100644 data/shaders/glsl/graphicspipelinelibrary/shared.vert create mode 100644 data/shaders/glsl/graphicspipelinelibrary/shared.vert.spv create mode 100644 data/shaders/glsl/graphicspipelinelibrary/uber.frag create mode 100644 data/shaders/glsl/graphicspipelinelibrary/uber.frag.spv diff --git a/data/shaders/glsl/graphicspipelinelibrary/shared.vert b/data/shaders/glsl/graphicspipelinelibrary/shared.vert new file mode 100644 index 00000000..6f8ed2da --- /dev/null +++ b/data/shaders/glsl/graphicspipelinelibrary/shared.vert @@ -0,0 +1,33 @@ +#version 450 + +layout (location = 0) in vec3 inPos; +layout (location = 1) in vec3 inNormal; +layout (location = 2) in vec3 inColor; + +layout (binding = 0) uniform UBO +{ + mat4 projection; + mat4 model; + vec4 lightPos; +} ubo; + +layout (location = 0) out vec3 outNormal; +layout (location = 1) out vec3 outColor; +layout (location = 2) out vec3 outViewVec; +layout (location = 3) out vec3 outLightVec; +layout (location = 4) flat out vec3 outFlatNormal; + +void main() +{ + outNormal = inNormal; + outColor = inColor; + vec4 pos = vec4(inPos.xyz, 1.0); + + gl_Position = ubo.projection * ubo.model * pos; + + pos = ubo.model * pos; + outNormal = mat3(ubo.model) * inNormal; + vec3 lPos = ubo.lightPos.xyz; + outLightVec = lPos - pos.xyz; + outViewVec = -pos.xyz; +} \ No newline at end of file diff --git a/data/shaders/glsl/graphicspipelinelibrary/shared.vert.spv b/data/shaders/glsl/graphicspipelinelibrary/shared.vert.spv new file mode 100644 index 0000000000000000000000000000000000000000..b45272b3508d64126724a5947ef8b42066aefa51 GIT binary patch literal 2548 zcmZ9M+ip`w6o!X5HmA0n4{d2k2&EiT%Bi#z2nmn?DKSOl7KN|{8#&&Q;|SE7BJ~O2 zk@{4({LHuj9;`?Z(LD&XjhfJ!x0^BMsJpGy;>--qgzb+`{vPsrCNr zscY9S>+w};WJi7Wp}9uE>T=mt-VOGGFM-xW;3Sv_zk_F>!Ec29ACfeQ9*%Fqn4{Y0 zKdpMJ%N=AnbC%srDs$GrZ?@`GJw&+CT(j&Bs3vs|!mm~9*=8?gw~eN6D}BfQrt`90 z^p=WVzxaR(JK&YIRqJKHth%r}v%lxgeA(?kFaIp`3!7)cHrpxJ?w9NRh`JKSw#Fim7ImkJg(r(C+8lVIpn<4;FRTjOLJJvr{6q`_v&f_{X>s^nB#*; z`?KF5&bq^K>c&ljh?Coj8|1gPejkDTD9d?v=W+_TLZ29@S@&D{*=5?DjsS<7iZQ_IFg zjdRG>)czy8{aDAEKeKyRa@z0Jx?IH1B1iuFwQRigovm-l_|Sbz!}{R=IHen$$vmFV zrO&@GzI|hzoByTpPV8l-)^B5%{{r|n?hx%8QV-icWZ#80--~+K4kP;po>9$N9tCo) z;Eo~7?HJ;Y*PMN~>A-V;4UE@cyKl*w_O9(7HJrl_z}ZXy`#8mZ7RZOsd1QT@QTSXy zmj6s1V=e;YrEi%TI2;e*I$6Va5)3-JYs%DHby>TZXwGH*Y=p-;EXX( z#N0-f7p{nD^D6un?f`o+x4zzk{oMt=JMUY48t5zUJ@^)k2ah>=JI=2{|~R5;D7p(o5uhE literal 0 HcmV?d00001 diff --git a/data/shaders/glsl/graphicspipelinelibrary/uber.frag b/data/shaders/glsl/graphicspipelinelibrary/uber.frag new file mode 100644 index 00000000..c60a2901 --- /dev/null +++ b/data/shaders/glsl/graphicspipelinelibrary/uber.frag @@ -0,0 +1,61 @@ +#version 450 +layout (location = 0) in vec3 inNormal; +layout (location = 1) in vec3 inColor; +layout (location = 2) in vec3 inViewVec; +layout (location = 3) in vec3 inLightVec; +layout (location = 4) flat in vec3 inFlatNormal; + +layout (constant_id = 0) const int LIGHTING_MODEL = 0; + +layout (location = 0) out vec4 outFragColor; + +void main() +{ + switch (LIGHTING_MODEL) { + case 0: // Phong + { + vec3 ambient = inColor * vec3(0.25); + vec3 N = normalize(inNormal); + vec3 L = normalize(inLightVec); + vec3 V = normalize(inViewVec); + vec3 R = reflect(-L, N); + vec3 diffuse = max(dot(N, L), 0.0) * inColor; + vec3 specular = pow(max(dot(R, V), 0.0), 32.0) * vec3(0.75); + outFragColor = vec4(ambient + diffuse * 1.75 + specular, 1.0); + break; + } + case 1: // Toon + { + + vec3 N = normalize(inNormal); + vec3 L = normalize(inLightVec); + float intensity = dot(N,L); + vec3 color; + if (intensity > 0.98) + color = inColor * 1.5; + else if (intensity > 0.9) + color = inColor * 1.0; + else if (intensity > 0.5) + color = inColor * 0.6; + else if (intensity > 0.25) + color = inColor * 0.4; + else + color = inColor * 0.2; + outFragColor.rgb = color; + break; + } + case 2: // No shading + { + outFragColor.rgb = inColor; + break; + } + case 3: // Greyscale + { + outFragColor.rgb = vec3(dot(inColor.rgb, vec3(0.299, 0.587, 0.114))); + break; + } + } + + // Scene is dark, brigthen up a bit + outFragColor.rgb *= 1.25; +} \ No newline at end of file diff --git a/data/shaders/glsl/graphicspipelinelibrary/uber.frag.spv b/data/shaders/glsl/graphicspipelinelibrary/uber.frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..3131246ce05dc3eb87e1c380da9b94f8168dbf4d GIT binary patch literal 3984 zcmZ9OS#woI6op$t5FyME#PK4|ASwbPL&6-;B#Z_p#1Im=n%rP=!=yq$k#fRPK?Su6 zan^s}ljSo$`mT@u0;jUd?>qPGa4Jph>b2G$dUyA^9qPu+yEdh3(u6cV{g7&7S{j3r z(!?|_n_F6Uv@RU1?pwHc$pc1APIXzJF;j>d%dGWvmMc1@W7lIhVI^!O_B}S0JmXjp zRL3eQ{Y;U1)M9=;>l)L#xnG zrP=Uyte$`NP_DH15A=2R0yF=O%#_Of_E)a7miHg1c64{u{I{ZYg^l=qK(V;-ak~`IMBJj)?}vFrAL`-eb%Km zaP+hdT&`5RD}&|g(JXdsdYZW_+hMJyXIZXn?Cq>x-CGMM=$UyZ4!LJ@3#KObJme0! z`0&d7rFO#HNGyp!^yeN9`t!{Gq_1}=4Q8=GCT{ z+4!t0x0e!p3bV25@@dT0k*~z;rF}U^!u^qncTpxg1#G^(oZ9GI$=FMJtMrF`O^rmz%O2yGr|wSf4Op{Db_ib z@zy5q_!V#obLf{J&sc4rC&2D0`qY04^SmSeG}!Ma>c0;@(|BQIy$|4*H#Cm)a}NIN zkv~?&d7p=yW1POIa|xW%rRKP2zggE^$}iLPybFi@{e@D`Y^>j&?~LageD)~rJ5u!f zZWQ^vQQWtp==ZHCa^Hg@Z!2)`+IOMo_f3%hN%sxRj(>TJ)q;-)yWi_D_d5Y|uM;u% ztA7>y^|vwu^Y?U`Q;;|2n!lkYvOEv9h@S)Ychr#o)@}g1)&@+UIi8t%TVi?#H)Q-`_<0d-3<+?gM|ns2M+r`Eg9`N6u;&^Aow&!R*`c6tm+W;`Kd=?ZMoy zIM#gzJROVsdlsy=8*^RX8#T|%n&w+4`gjgp>|-xntrN44easH~(AS0S$E+icKDxoh zK6>D4ac}maW*^ox-#XF90dTR8=izE)%szUV9rmH`1?(VZ9dY#02QK!}4_B*TaUN>+ zVNLU`6Mei0F7`11SDT61N0r%OANmHdBbar>(Z>+D*vDbG+953ZP_qwfns1%RKMF49 zzXVr1hDE+wG2c4HeYrn*oVT+)z60l2?`5!ew1)5T1oJSq5<8B?@AM?tIEOt9Gpj|; zt6+0ZVUhD1*c^XP<`|lmZ #define ENABLE_VALIDATION false @@ -18,14 +17,12 @@ class VulkanExample: public VulkanExampleBase public: vkglTF::Model scene; - vks::Buffer uniformBuffer; - - // Same uniform buffer layout as shader struct UBOVS { glm::mat4 projection; glm::mat4 modelView; glm::vec4 lightPos = glm::vec4(0.0f, 2.0f, 1.0f, 0.0f); } uboVS; + vks::Buffer uniformBuffer; VkPipelineLayout pipelineLayout; VkDescriptorSet descriptorSet; @@ -33,25 +30,37 @@ public: VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT graphicsPipelineLibraryFeatures{}; - struct { - VkPipeline phong; - VkPipeline wireframe; - VkPipeline toon; - } pipelines; + struct PipelineLibrary { + VkPipeline vertexInputInterface; + VkPipeline preRasterizationShaders; + VkPipeline fragmentOutputInterface; + } pipelineLibrary; + + std::vector pipelines{}; struct ShaderInfo { uint32_t* code; size_t size; }; + std::mutex mutex; + VkPipelineCache threadPipelineCache{ VK_NULL_HANDLE }; + + bool newPipelineCreated = false; + + uint32_t splitX{ 2 }; + uint32_t splitY{ 2 }; + + std::vector colors{}; + float rotation{ 0.0f }; + VulkanExample() : VulkanExampleBase(ENABLE_VALIDATION) { title = "Graphics pipeline library"; camera.type = Camera::CameraType::lookat; - camera.setPosition(glm::vec3(0.0f, 0.0f, -10.5f)); + camera.setPosition(glm::vec3(0.0f, 0.0f, -2.0f)); camera.setRotation(glm::vec3(-25.0f, 15.0f, 0.0f)); camera.setRotationSpeed(0.5f); - camera.setPerspective(60.0f, (float)(width / 3.0f) / (float)height, 0.1f, 256.0f); // Enable required extensions enabledInstanceExtensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); @@ -66,32 +75,14 @@ public: ~VulkanExample() { - // Clean up used Vulkan resources - // Note : Inherited destructor cleans up resources stored in base class - vkDestroyPipeline(device, pipelines.phong, nullptr); - if (deviceFeatures.fillModeNonSolid) - { - vkDestroyPipeline(device, pipelines.wireframe, nullptr); - } - vkDestroyPipeline(device, pipelines.toon, nullptr); - - vkDestroyPipelineLayout(device, pipelineLayout, nullptr); - vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); - - uniformBuffer.destroy(); - } - - // Enable physical device features required for this example - virtual void getEnabledFeatures() - { - // Fill mode non solid is required for wireframe display - if (deviceFeatures.fillModeNonSolid) { - enabledFeatures.fillModeNonSolid = VK_TRUE; - // Wide lines must be present for line width > 1.0f - if (deviceFeatures.wideLines) { - enabledFeatures.wideLines = VK_TRUE; + if (device) { + for (auto pipeline : pipelines) { + vkDestroyPipeline(device, pipeline, nullptr); } - }; + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + uniformBuffer.destroy(); + } } void buildCommandBuffers() @@ -113,45 +104,44 @@ public: for (int32_t i = 0; i < drawCmdBuffers.size(); ++i) { - // Set target frame buffer renderPassBeginInfo.framebuffer = frameBuffers[i]; VK_CHECK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo)); vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); - VkViewport viewport = vks::initializers::viewport((float)width, (float)height, 0.0f, 1.0f); - vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport); - - VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0); - vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor); - vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, NULL); scene.bindBuffers(drawCmdBuffers[i]); - // Left : Solid colored - viewport.width = (float)width / 3.0; - vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport); - vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.phong); - scene.draw(drawCmdBuffers[i]); + // Render a viewport for each pipeline + float w = (float)width / (float)splitX; + float h = (float)height / (float)splitY; + uint32_t idx = 0; + for (uint32_t y = 0; y < splitX; y++) { + for (uint32_t x = 0; x < splitY; x++) { + VkViewport viewport{}; + viewport.x = w * (float)x; + viewport.y = h * (float)y; + viewport.width = w; + viewport.height = h; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport); - // Center : Toon - viewport.x = (float)width / 3.0; - vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport); - vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.toon); - // Line width > 1.0f only if wide lines feature is supported - if (deviceFeatures.wideLines) { - vkCmdSetLineWidth(drawCmdBuffers[i], 2.0f); - } - scene.draw(drawCmdBuffers[i]); + VkRect2D scissor{}; + scissor.extent.width = (uint32_t)w; + scissor.extent.height = (uint32_t)h; + scissor.offset.x = (uint32_t)w * x; + scissor.offset.y = (uint32_t)h * y; + vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor); - if (deviceFeatures.fillModeNonSolid) - { - // Right : Wireframe - viewport.x = (float)width / 3.0 + (float)width / 3.0; - vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport); - vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.wireframe); - scene.draw(drawCmdBuffers[i]); + if (pipelines.size() > idx) { + vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines[idx]); + scene.draw(drawCmdBuffers[i]); + } + + idx++; + } } drawUI(drawCmdBuffers[i]); @@ -165,108 +155,58 @@ public: void loadAssets() { const uint32_t glTFLoadingFlags = vkglTF::FileLoadingFlags::PreTransformVertices | vkglTF::FileLoadingFlags::PreMultiplyVertexColors | vkglTF::FileLoadingFlags::FlipY; - scene.loadFromFile(getAssetPath() + "models/treasure_smooth.gltf", vulkanDevice, queue, glTFLoadingFlags); + scene.loadFromFile(getAssetPath() + "models/color_teapot_spheres.gltf", vulkanDevice, queue, glTFLoadingFlags); } void setupDescriptorPool() { - std::vector poolSizes = - { + std::vector poolSizes = { vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1) }; - - VkDescriptorPoolCreateInfo descriptorPoolInfo = - vks::initializers::descriptorPoolCreateInfo( - poolSizes.size(), - poolSizes.data(), - 2); - + VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, 2); VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool)); } void setupDescriptorSetLayout() { - std::vector setLayoutBindings = - { - // Binding 0 : Vertex shader uniform buffer - vks::initializers::descriptorSetLayoutBinding( - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - VK_SHADER_STAGE_VERTEX_BIT, - 0) + std::vector setLayoutBindings = { + vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0) }; - - VkDescriptorSetLayoutCreateInfo descriptorLayout = - vks::initializers::descriptorSetLayoutCreateInfo( - setLayoutBindings.data(), - setLayoutBindings.size()); - + VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings); VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout)); - VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = - vks::initializers::pipelineLayoutCreateInfo( - &descriptorSetLayout, - 1); - + VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1); VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pPipelineLayoutCreateInfo, nullptr, &pipelineLayout)); } void setupDescriptorSet() { - VkDescriptorSetAllocateInfo allocInfo = - vks::initializers::descriptorSetAllocateInfo( - descriptorPool, - &descriptorSetLayout, - 1); - + VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1); VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet)); - std::vector writeDescriptorSets = - { - // Binding 0 : Vertex shader uniform buffer - vks::initializers::writeDescriptorSet( - descriptorSet, - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - 0, - &uniformBuffer.descriptor) + std::vector writeDescriptorSets = { + vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffer.descriptor) }; - - vkUpdateDescriptorSets(device, writeDescriptorSets.size(), writeDescriptorSets.data(), 0, NULL); + vkUpdateDescriptorSets(device, writeDescriptorSets.size(), writeDescriptorSets.data(), 0, nullptr); } // With VK_EXT_graphics_pipeline_library we don't need to create the shader module when loading it, but instead have the driver create it at linking time // So we use a custom function that only loads the required shader information without actually creating the shader module - -#if defined(__ANDROID__) - // Android shaders are stored as assets in the apk so they need to be loaded via the asset manager - bool loadShader(AAssetManager* assetManager, std::string fileName, const uint32_t** pShaderCode, size_t& shaderSize) + bool loadShaderFile(std::string fileName, ShaderInfo &shaderInfo) { +#if defined(__ANDROID__) // Load shader from compressed asset - AAsset* asset = AAssetManager_open(assetManager, fileName, AASSET_MODE_STREAMING); + // @todo + AAsset* asset = AAssetManager_open(androidApp->activity->assetManager, fileName, AASSET_MODE_STREAMING); assert(asset); size_t size = AAsset_getLength(asset); assert(size > 0); - char* shaderCode = new char[size]; + shaderInfo.size = size; + shaderInfo.code = new uint32_t[size / 4]; AAsset_read(asset, shaderCode, size); AAsset_close(asset); - - VkShaderModule shaderModule; - VkShaderModuleCreateInfo moduleCreateInfo; - moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - moduleCreateInfo.pNext = NULL; - moduleCreateInfo.codeSize = size; - moduleCreateInfo.pCode = (uint32_t*)shaderCode; - moduleCreateInfo.flags = 0; - - VK_CHECK_RESULT(vkCreateShaderModule(device, &moduleCreateInfo, NULL, &shaderModule)); - - delete[] shaderCode; - - return shaderModule; - } #else - bool loadShaderFile(std::string fileName, ShaderInfo &shaderInfo) - { std::ifstream is(fileName, std::ios::binary | std::ios::in | std::ios::ate); if (is.is_open()) @@ -279,223 +219,209 @@ public: return true; } else { std::cerr << "Error: Could not open shader file \"" << fileName << "\"" << "\n"; + throw std::runtime_error("Could open shader file"); return false; } - } #endif - - VkPipeline createVertexInputState(VkDevice device, VkPipelineCache vertexShaderCache, VkPipelineLayout layout) - { - VkGraphicsPipelineLibraryCreateInfoEXT libraryInfo{}; - libraryInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_LIBRARY_CREATE_INFO_EXT; - libraryInfo.flags = VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT; - - VkPipelineVertexInputStateCreateInfo vertexInputState = *vkglTF::Vertex::getPipelineVertexInputState({ vkglTF::VertexComponent::Position, vkglTF::VertexComponent::Normal, vkglTF::VertexComponent::Color }); - VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE); - - VkGraphicsPipelineCreateInfo pipelineCI{}; - pipelineCI.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - pipelineCI.pNext = &libraryInfo; - pipelineCI.pInputAssemblyState = &inputAssemblyState; - pipelineCI.pVertexInputState = &vertexInputState; - - VkPipeline library = VK_NULL_HANDLE; - VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, vertexShaderCache, 1, &pipelineCI, nullptr, &library)); - return library; } - VkPipeline createVertexShader(VkDevice device, const ShaderInfo shaderInfo, VkPipelineCache vertexShaderCache, VkPipelineLayout layout) + // Create the shared pipeline parts up-front + void preparePipelineLibrary() { - VkGraphicsPipelineLibraryCreateInfoEXT libraryInfo{}; - libraryInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_LIBRARY_CREATE_INFO_EXT; - libraryInfo.flags = VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT; + // Create a pipeline library for the vertex input interface + { + VkGraphicsPipelineLibraryCreateInfoEXT libraryInfo{}; + libraryInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_LIBRARY_CREATE_INFO_EXT; + libraryInfo.flags = VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT; - VkDynamicState vertexDynamicStates[2] = { - VK_DYNAMIC_STATE_VIEWPORT, - VK_DYNAMIC_STATE_SCISSOR }; + VkPipelineVertexInputStateCreateInfo vertexInputState = *vkglTF::Vertex::getPipelineVertexInputState({ vkglTF::VertexComponent::Position, vkglTF::VertexComponent::Normal, vkglTF::VertexComponent::Color }); + VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE); - VkPipelineDynamicStateCreateInfo dynamicInfo{}; - dynamicInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; - dynamicInfo.dynamicStateCount = 2; - dynamicInfo.pDynamicStates = vertexDynamicStates; + VkGraphicsPipelineCreateInfo pipelineCI{}; + pipelineCI.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineCI.flags = VK_PIPELINE_CREATE_LIBRARY_BIT_KHR | VK_PIPELINE_CREATE_RETAIN_LINK_TIME_OPTIMIZATION_INFO_BIT_EXT; + pipelineCI.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineCI.pNext = &libraryInfo; + pipelineCI.pInputAssemblyState = &inputAssemblyState; + pipelineCI.pVertexInputState = &vertexInputState; + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelineLibrary.vertexInputInterface)); + } - VkPipelineViewportStateCreateInfo viewportState = {}; - viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; - viewportState.viewportCount = 1; - viewportState.scissorCount = 1; + // Creata a pipeline library for the vertex shader stage + { + VkGraphicsPipelineLibraryCreateInfoEXT libraryInfo{}; + libraryInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_LIBRARY_CREATE_INFO_EXT; + libraryInfo.flags = VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT; - VkPipelineRasterizationStateCreateInfo rasterizationState = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_BACK_BIT, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0); + VkDynamicState vertexDynamicStates[2] = { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR }; - VkShaderModuleCreateInfo shaderModuleCreateInfo{}; - shaderModuleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - shaderModuleCreateInfo.codeSize = shaderInfo.size; - shaderModuleCreateInfo.pCode = shaderInfo.code; + VkPipelineDynamicStateCreateInfo dynamicInfo{}; + dynamicInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamicInfo.dynamicStateCount = 2; + dynamicInfo.pDynamicStates = vertexDynamicStates; - VkPipelineShaderStageCreateInfo shaderStageCreateInfo{}; - shaderStageCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - shaderStageCreateInfo.pNext = &shaderModuleCreateInfo; - shaderStageCreateInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; - shaderStageCreateInfo.pName = "main"; + VkPipelineViewportStateCreateInfo viewportState = {}; + viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState.viewportCount = 1; + viewportState.scissorCount = 1; - VkGraphicsPipelineCreateInfo pipelineCI{}; - pipelineCI.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - pipelineCI.pNext = &libraryInfo; - pipelineCI.renderPass = renderPass; - pipelineCI.flags = VK_PIPELINE_CREATE_LIBRARY_BIT_KHR | VK_PIPELINE_CREATE_RETAIN_LINK_TIME_OPTIMIZATION_INFO_BIT_EXT; - pipelineCI.stageCount = 1; - pipelineCI.pStages = &shaderStageCreateInfo; - pipelineCI.layout = layout; - pipelineCI.pDynamicState = &dynamicInfo; - pipelineCI.pViewportState = &viewportState; - pipelineCI.pRasterizationState = &rasterizationState; + VkPipelineRasterizationStateCreateInfo rasterizationState = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_BACK_BIT, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0); - VkPipeline library = VK_NULL_HANDLE; - VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, vertexShaderCache, 1, &pipelineCI, nullptr, &library)); - return library; + // @todo: we can skip the pipeline shader module info and directly consume the shader module + ShaderInfo shaderInfo{}; + loadShaderFile(getShadersPath() + "graphicspipelinelibrary/shared.vert.spv", shaderInfo); + + VkShaderModuleCreateInfo shaderModuleCI{}; + shaderModuleCI.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + shaderModuleCI.codeSize = shaderInfo.size; + shaderModuleCI.pCode = shaderInfo.code; + + VkPipelineShaderStageCreateInfo shaderStageCI{}; + shaderStageCI.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shaderStageCI.pNext = &shaderModuleCI; + shaderStageCI.stage = VK_SHADER_STAGE_VERTEX_BIT; + shaderStageCI.pName = "main"; + + VkGraphicsPipelineCreateInfo pipelineCI{}; + pipelineCI.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineCI.pNext = &libraryInfo; + pipelineCI.renderPass = renderPass; + pipelineCI.flags = VK_PIPELINE_CREATE_LIBRARY_BIT_KHR | VK_PIPELINE_CREATE_RETAIN_LINK_TIME_OPTIMIZATION_INFO_BIT_EXT; + pipelineCI.stageCount = 1; + pipelineCI.pStages = &shaderStageCI; + pipelineCI.layout = pipelineLayout; + pipelineCI.pDynamicState = &dynamicInfo; + pipelineCI.pViewportState = &viewportState; + pipelineCI.pRasterizationState = &rasterizationState; + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelineLibrary.preRasterizationShaders)); + } + + // Create a pipeline library for the fragment output interface + { + VkGraphicsPipelineLibraryCreateInfoEXT libraryInfo{}; + libraryInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_LIBRARY_CREATE_INFO_EXT; + libraryInfo.flags = VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT; + + VkPipelineColorBlendAttachmentState blendAttachmentSstate = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE); + VkPipelineColorBlendStateCreateInfo colorBlendState = vks::initializers::pipelineColorBlendStateCreateInfo(1, &blendAttachmentSstate); + VkPipelineMultisampleStateCreateInfo multisampleState = vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT); + + VkGraphicsPipelineCreateInfo pipelineLibraryCI{}; + pipelineLibraryCI.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineLibraryCI.pNext = &libraryInfo; + pipelineLibraryCI.layout = pipelineLayout; + pipelineLibraryCI.renderPass = renderPass; + pipelineLibraryCI.pColorBlendState = &colorBlendState; + pipelineLibraryCI.pMultisampleState = &multisampleState; + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineLibraryCI, nullptr, &pipelineLibrary.fragmentOutputInterface)); + } } - VkPipeline createFragmentShader(VkDevice device, const ShaderInfo shaderInfo, VkPipelineCache vertexShaderCache, VkPipelineLayout layout) + void threadFn() { + const std::lock_guard lock(mutex); + + auto start = std::chrono::steady_clock::now(); + + prepareNewPipeline(); + newPipelineCreated = true; + + // Change viewport/draw count + if (pipelines.size() > splitX * splitY) { + splitX++; + splitY++; + } + + auto delta = std::chrono::duration_cast(std::chrono::steady_clock::now() - start); + std::cout << "Pipeline created in " << delta.count() << " microseconds\n"; + } + + // Create a new pipeline using the pipeline library and a customized fragment shader + // Used from a thread + void prepareNewPipeline() + { + // Create the fragment shader part of the pipeline library with some random options VkGraphicsPipelineLibraryCreateInfoEXT libraryInfo{}; libraryInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_LIBRARY_CREATE_INFO_EXT; libraryInfo.flags = VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT; VkPipelineDepthStencilStateCreateInfo depthStencilState = vks::initializers::pipelineDepthStencilStateCreateInfo(VK_TRUE, VK_TRUE, VK_COMPARE_OP_LESS_OR_EQUAL); - VkPipelineMultisampleStateCreateInfo multisampleState = vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT); + VkPipelineMultisampleStateCreateInfo multisampleState = vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT); - VkShaderModuleCreateInfo shaderModuleCreateInfo{}; - shaderModuleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - shaderModuleCreateInfo.codeSize = shaderInfo.size; - shaderModuleCreateInfo.pCode = shaderInfo.code; + // Using the pipeline library extension, we can skip the pipeline shader module creation and directly pass the shader code to the pipeline + ShaderInfo shaderInfo{}; + loadShaderFile(getShadersPath() + "graphicspipelinelibrary/uber.frag.spv", shaderInfo); - VkPipelineShaderStageCreateInfo shaderStageCreateInfo{}; - shaderStageCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - shaderStageCreateInfo.pNext = &shaderModuleCreateInfo; - shaderStageCreateInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; - shaderStageCreateInfo.pName = "main"; + VkShaderModuleCreateInfo shaderModuleCI{}; + shaderModuleCI.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + shaderModuleCI.codeSize = shaderInfo.size; + shaderModuleCI.pCode = shaderInfo.code; + + VkPipelineShaderStageCreateInfo shaderStageCI{}; + shaderStageCI.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shaderStageCI.pNext = &shaderModuleCI; + shaderStageCI.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + shaderStageCI.pName = "main"; + + // Select lighting model using a specialization constant + srand((unsigned int)time(NULL)); + uint32_t lighting_model = (int)(rand() % 4); + + // Each shader constant of a shader stage corresponds to one map entry + VkSpecializationMapEntry specializationMapEntry{}; + specializationMapEntry.constantID = 0; + specializationMapEntry.size = sizeof(uint32_t); + + VkSpecializationInfo specializationInfo{}; + specializationInfo.mapEntryCount = 1; + specializationInfo.pMapEntries = &specializationMapEntry; + specializationInfo.dataSize = sizeof(uint32_t); + specializationInfo.pData = &lighting_model; + + shaderStageCI.pSpecializationInfo = &specializationInfo; VkGraphicsPipelineCreateInfo pipelineCI{}; pipelineCI.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; pipelineCI.pNext = &libraryInfo; pipelineCI.flags = VK_PIPELINE_CREATE_LIBRARY_BIT_KHR | VK_PIPELINE_CREATE_RETAIN_LINK_TIME_OPTIMIZATION_INFO_BIT_EXT; pipelineCI.stageCount = 1; - pipelineCI.pStages = &shaderStageCreateInfo; - pipelineCI.layout = layout; + pipelineCI.pStages = &shaderStageCI; + pipelineCI.layout = pipelineLayout; pipelineCI.renderPass = renderPass; pipelineCI.pDepthStencilState = &depthStencilState; pipelineCI.pMultisampleState = &multisampleState; + VkPipeline fragment_shader = VK_NULL_HANDLE; + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, threadPipelineCache, 1, &pipelineCI, nullptr, &fragment_shader)); - VkPipeline library = VK_NULL_HANDLE; - VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, vertexShaderCache, 1, &pipelineCI, nullptr, &library)); - return library; - } + // Create the pipeline using the pre-built pipeline library parts + // Except for above fragment shader part all parts have been pre-built and will be re-used + std::vector libraries = { + pipelineLibrary.vertexInputInterface, + pipelineLibrary.preRasterizationShaders, + fragment_shader, + pipelineLibrary.fragmentOutputInterface }; - VkPipeline createFragmentOutputState(VkDevice device, VkPipelineCache vertexShaderCache, VkPipelineLayout layout) - { - VkGraphicsPipelineLibraryCreateInfoEXT libraryInfo{}; - libraryInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_LIBRARY_CREATE_INFO_EXT; - libraryInfo.flags = VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT; + // Link the library parts into a graphics pipeline + VkPipelineLibraryCreateInfoKHR pipelineLibraryCI{}; + pipelineLibraryCI.sType = VK_STRUCTURE_TYPE_PIPELINE_LIBRARY_CREATE_INFO_KHR; + pipelineLibraryCI.libraryCount = static_cast(libraries.size()); + pipelineLibraryCI.pLibraries = libraries.data(); - VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE); - VkPipelineColorBlendStateCreateInfo colorBlendState = vks::initializers::pipelineColorBlendStateCreateInfo(1, &blendAttachmentState); - VkPipelineMultisampleStateCreateInfo multisampleState = vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT); + // If set to true, we pass VK_PIPELINE_CREATE_LINK_TIME_OPTIMIZATION_BIT_EXT which will let the implementation do additional optimizations at link time + // This trades in pipeline creation time for run-time performance + bool optimized = true; - VkGraphicsPipelineCreateInfo pipelineCI{}; - pipelineCI.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - pipelineCI.pNext = &libraryInfo; - pipelineCI.flags = VK_PIPELINE_CREATE_LIBRARY_BIT_KHR | VK_PIPELINE_CREATE_RETAIN_LINK_TIME_OPTIMIZATION_INFO_BIT_EXT; - pipelineCI.layout = layout; - pipelineCI.renderPass = renderPass; - pipelineCI.pColorBlendState = &colorBlendState; - pipelineCI.pMultisampleState = &multisampleState; - - VkPipeline library = VK_NULL_HANDLE; - VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, vertexShaderCache, 1, &pipelineCI, nullptr, &library)); - return library; - } - - VkPipeline linkExecutable(VkDevice device, const std::vector libraries, VkPipelineCache executableCache, bool optimized) - { - VkPipelineLibraryCreateInfoKHR linkingInfo{}; - linkingInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LIBRARY_CREATE_INFO_KHR; - linkingInfo.libraryCount = static_cast(libraries.size()); - linkingInfo.pLibraries = libraries.data(); - - VkGraphicsPipelineCreateInfo executablePipelineCreateInfo{}; - executablePipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - executablePipelineCreateInfo.pNext = &linkingInfo; - executablePipelineCreateInfo.flags |= optimized ? VK_PIPELINE_CREATE_LINK_TIME_OPTIMIZATION_BIT_EXT : 0; + VkGraphicsPipelineCreateInfo executablePipelineCI{}; + executablePipelineCI.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + executablePipelineCI.pNext = &pipelineLibraryCI; + executablePipelineCI.flags |= optimized ? VK_PIPELINE_CREATE_LINK_TIME_OPTIMIZATION_BIT_EXT : 0; VkPipeline executable = VK_NULL_HANDLE; - VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, executableCache, 1, &executablePipelineCreateInfo, nullptr, &executable)); - return executable; - } + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, threadPipelineCache, 1, &executablePipelineCI, nullptr, &executable)); - void preparePipelines() - { - struct Shaders { - ShaderInfo phongVS; - ShaderInfo phongFS; - } shaders; - loadShaderFile(getShadersPath() + "pipelines/phong.vert.spv", shaders.phongVS); - loadShaderFile(getShadersPath() + "pipelines/phong.frag.spv", shaders.phongFS); - VkPipelineShaderStageCreateInfo vsShader = loadShader(getShadersPath() + "pipelines/phong.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); - VkPipelineShaderStageCreateInfo fsShader = loadShader(getShadersPath() + "pipelines/phong.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); - std::vector libraries = { - createVertexInputState(device, pipelineCache, pipelineLayout), - createVertexShader(device, shaders.phongVS, pipelineCache, pipelineLayout), - createFragmentShader(device, shaders.phongFS, pipelineCache, pipelineLayout), - createFragmentOutputState(device, pipelineCache, pipelineLayout), - }; - VkPipeline compiledPipeline = linkExecutable(device, libraries, pipelineCache, true); - - VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE); - VkPipelineRasterizationStateCreateInfo rasterizationState = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_BACK_BIT, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0); - VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE); - VkPipelineColorBlendStateCreateInfo colorBlendState = vks::initializers::pipelineColorBlendStateCreateInfo(1, &blendAttachmentState); - VkPipelineDepthStencilStateCreateInfo depthStencilState = vks::initializers::pipelineDepthStencilStateCreateInfo(VK_TRUE, VK_TRUE, VK_COMPARE_OP_LESS_OR_EQUAL); - VkPipelineViewportStateCreateInfo viewportState = vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0); - VkPipelineMultisampleStateCreateInfo multisampleState = vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT); - std::vector dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, VK_DYNAMIC_STATE_LINE_WIDTH, }; - VkPipelineDynamicStateCreateInfo dynamicState = vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables); - std::array shaderStages; - - VkGraphicsPipelineCreateInfo pipelineCI = vks::initializers::pipelineCreateInfo(pipelineLayout, renderPass); - pipelineCI.pInputAssemblyState = &inputAssemblyState; - pipelineCI.pRasterizationState = &rasterizationState; - pipelineCI.pColorBlendState = &colorBlendState; - pipelineCI.pMultisampleState = &multisampleState; - pipelineCI.pViewportState = &viewportState; - pipelineCI.pDepthStencilState = &depthStencilState; - pipelineCI.pDynamicState = &dynamicState; - pipelineCI.stageCount = shaderStages.size(); - pipelineCI.pStages = shaderStages.data(); - pipelineCI.pVertexInputState = vkglTF::Vertex::getPipelineVertexInputState({vkglTF::VertexComponent::Position, vkglTF::VertexComponent::Normal, vkglTF::VertexComponent::Color}); - - // Create the graphics pipeline state objects - - // Textured pipeline - // Phong shading pipeline - //shaderStages[0] = loadShader(getShadersPath() + "pipelines/phong.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); - //shaderStages[1] = loadShader(getShadersPath() + "pipelines/phong.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); - //VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.phong)); - - pipelines.phong = compiledPipeline; - - // Toon shading pipeline - shaderStages[0] = loadShader(getShadersPath() + "pipelines/toon.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); - shaderStages[1] = loadShader(getShadersPath() + "pipelines/toon.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); - VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.toon)); - - // Pipeline for wire frame rendering - // Non solid rendering is not a mandatory Vulkan feature - if (deviceFeatures.fillModeNonSolid) - { - rasterizationState.polygonMode = VK_POLYGON_MODE_LINE; - shaderStages[0] = loadShader(getShadersPath() + "pipelines/wireframe.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); - shaderStages[1] = loadShader(getShadersPath() + "pipelines/wireframe.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); - VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.wireframe)); - } + pipelines.push_back(executable); } // Prepare and initialize uniform buffer containing shader uniforms @@ -516,8 +442,12 @@ public: void updateUniformBuffers() { + if (!paused) { + rotation += frameTimer * 0.1f; + } + camera.setPerspective(45.0f, ((float)width / (float)splitX) / ((float)height / (float)splitY), 0.1f, 256.0f); uboVS.projection = camera.matrices.perspective; - uboVS.modelView = camera.matrices.view; + uboVS.modelView = camera.matrices.view * glm::rotate(glm::mat4(1.0f), glm::radians(rotation * 360.0f), glm::vec3(0.0f, 1.0f, 0.0f)); memcpy(uniformBuffer.mapped, &uboVS, sizeof(uboVS)); } @@ -538,10 +468,20 @@ public: loadAssets(); prepareUniformBuffers(); setupDescriptorSetLayout(); - preparePipelines(); + preparePipelineLibrary(); setupDescriptorPool(); setupDescriptorSet(); buildCommandBuffers(); + + // Create a separate pipeline cache for the pipeline creation thread + VkPipelineCacheCreateInfo pipelineCachCI = {}; + pipelineCachCI.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; + vkCreatePipelineCache(device, &pipelineCachCI, nullptr, &threadPipelineCache); + + // Create first pipeline using a background thread + std::thread pipelineGenerationThread(&VulkanExample::threadFn, this); + pipelineGenerationThread.detach(); + prepared = true; } @@ -549,18 +489,22 @@ public: { if (!prepared) return; - draw(); - if (camera.updated) { - updateUniformBuffers(); + if (newPipelineCreated) + { + newPipelineCreated = false; + vkQueueWaitIdle(queue); + buildCommandBuffers(); } + draw(); + updateUniformBuffers(); } virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay) { - if (!deviceFeatures.fillModeNonSolid) { - if (overlay->header("Info")) { - overlay->text("Non solid fill modes not supported!"); - } + if (overlay->button("New pipeline")) { + // Spwan a thread to create a new pipeline in the background + std::thread pipelineGenerationThread(&VulkanExample::threadFn, this); + pipelineGenerationThread.detach(); } } };