2022-04-03 18:22:53 +02:00
/*
* Vulkan Example - Using VK_EXT_graphics_pipeline_library
*
* Copyright ( C ) 2022 by Sascha Willems - www . saschawillems . de
*
* This code is licensed under the MIT license ( MIT ) ( http : //opensource.org/licenses/MIT)
*/
# include "vulkanexamplebase.h"
# include "VulkanglTFModel.h"
2022-04-22 08:05:28 +02:00
# include <thread>
2022-04-21 07:34:44 +02:00
# include <mutex>
2022-04-03 18:22:53 +02:00
# define ENABLE_VALIDATION false
class VulkanExample : public VulkanExampleBase
{
public :
2022-07-30 09:14:11 +02:00
bool linkTimeOptimization = true ;
2022-04-03 18:22:53 +02:00
vkglTF : : Model scene ;
struct UBOVS {
glm : : mat4 projection ;
glm : : mat4 modelView ;
2022-07-30 09:16:36 +02:00
glm : : vec4 lightPos = glm : : vec4 ( 0.0f , - 2.0f , 1.0f , 0.0f ) ;
2022-04-03 18:22:53 +02:00
} uboVS ;
2022-04-21 07:34:44 +02:00
vks : : Buffer uniformBuffer ;
2022-04-03 18:22:53 +02:00
VkPipelineLayout pipelineLayout ;
VkDescriptorSet descriptorSet ;
VkDescriptorSetLayout descriptorSetLayout ;
VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT graphicsPipelineLibraryFeatures { } ;
2022-04-21 07:34:44 +02:00
struct PipelineLibrary {
VkPipeline vertexInputInterface ;
VkPipeline preRasterizationShaders ;
VkPipeline fragmentOutputInterface ;
2022-07-30 09:14:11 +02:00
std : : vector < VkPipeline > fragmentShaders ;
2022-04-21 07:34:44 +02:00
} pipelineLibrary ;
std : : vector < VkPipeline > pipelines { } ;
2022-04-03 18:22:53 +02:00
2022-04-04 09:58:46 +02:00
struct ShaderInfo {
uint32_t * code ;
size_t size ;
} ;
2022-04-21 07:34:44 +02:00
std : : mutex mutex ;
VkPipelineCache threadPipelineCache { VK_NULL_HANDLE } ;
bool newPipelineCreated = false ;
uint32_t splitX { 2 } ;
uint32_t splitY { 2 } ;
std : : vector < glm : : vec3 > colors { } ;
float rotation { 0.0f } ;
2022-04-03 18:22:53 +02:00
VulkanExample ( ) : VulkanExampleBase ( ENABLE_VALIDATION )
{
title = " Graphics pipeline library " ;
camera . type = Camera : : CameraType : : lookat ;
2022-04-21 07:34:44 +02:00
camera . setPosition ( glm : : vec3 ( 0.0f , 0.0f , - 2.0f ) ) ;
2022-04-03 18:22:53 +02:00
camera . setRotation ( glm : : vec3 ( - 25.0f , 15.0f , 0.0f ) ) ;
camera . setRotationSpeed ( 0.5f ) ;
// Enable required extensions
enabledInstanceExtensions . push_back ( VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME ) ;
enabledDeviceExtensions . push_back ( VK_KHR_PIPELINE_LIBRARY_EXTENSION_NAME ) ;
enabledDeviceExtensions . push_back ( VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME ) ;
// Enable required extension features
graphicsPipelineLibraryFeatures . sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_FEATURES_EXT ;
graphicsPipelineLibraryFeatures . graphicsPipelineLibrary = VK_TRUE ;
deviceCreatepNextChain = & graphicsPipelineLibraryFeatures ;
}
~ VulkanExample ( )
{
2022-04-21 07:34:44 +02:00
if ( device ) {
for ( auto pipeline : pipelines ) {
vkDestroyPipeline ( device , pipeline , nullptr ) ;
2022-04-03 18:22:53 +02:00
}
2022-07-30 09:14:11 +02:00
for ( auto pipeline : pipelineLibrary . fragmentShaders ) {
vkDestroyPipeline ( device , pipeline , nullptr ) ;
}
vkDestroyPipeline ( device , pipelineLibrary . fragmentOutputInterface , nullptr ) ;
vkDestroyPipeline ( device , pipelineLibrary . preRasterizationShaders , nullptr ) ;
vkDestroyPipeline ( device , pipelineLibrary . vertexInputInterface , nullptr ) ;
vkDestroyPipelineCache ( device , threadPipelineCache , nullptr ) ;
2022-04-21 07:34:44 +02:00
vkDestroyPipelineLayout ( device , pipelineLayout , nullptr ) ;
vkDestroyDescriptorSetLayout ( device , descriptorSetLayout , nullptr ) ;
uniformBuffer . destroy ( ) ;
}
2022-04-03 18:22:53 +02:00
}
void buildCommandBuffers ( )
{
VkCommandBufferBeginInfo cmdBufInfo = vks : : initializers : : commandBufferBeginInfo ( ) ;
VkClearValue clearValues [ 2 ] ;
clearValues [ 0 ] . color = defaultClearColor ;
clearValues [ 1 ] . depthStencil = { 1.0f , 0 } ;
VkRenderPassBeginInfo renderPassBeginInfo = vks : : initializers : : renderPassBeginInfo ( ) ;
renderPassBeginInfo . renderPass = renderPass ;
renderPassBeginInfo . renderArea . offset . x = 0 ;
renderPassBeginInfo . renderArea . offset . y = 0 ;
renderPassBeginInfo . renderArea . extent . width = width ;
renderPassBeginInfo . renderArea . extent . height = height ;
renderPassBeginInfo . clearValueCount = 2 ;
renderPassBeginInfo . pClearValues = clearValues ;
for ( int32_t i = 0 ; i < drawCmdBuffers . size ( ) ; + + i )
{
renderPassBeginInfo . framebuffer = frameBuffers [ i ] ;
VK_CHECK_RESULT ( vkBeginCommandBuffer ( drawCmdBuffers [ i ] , & cmdBufInfo ) ) ;
vkCmdBeginRenderPass ( drawCmdBuffers [ i ] , & renderPassBeginInfo , VK_SUBPASS_CONTENTS_INLINE ) ;
vkCmdBindDescriptorSets ( drawCmdBuffers [ i ] , VK_PIPELINE_BIND_POINT_GRAPHICS , pipelineLayout , 0 , 1 , & descriptorSet , 0 , NULL ) ;
scene . bindBuffers ( drawCmdBuffers [ i ] ) ;
2022-04-21 07:34:44 +02:00
// 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 ) ;
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 ( pipelines . size ( ) > idx ) {
vkCmdBindPipeline ( drawCmdBuffers [ i ] , VK_PIPELINE_BIND_POINT_GRAPHICS , pipelines [ idx ] ) ;
scene . draw ( drawCmdBuffers [ i ] ) ;
}
idx + + ;
}
2022-04-03 18:22:53 +02:00
}
drawUI ( drawCmdBuffers [ i ] ) ;
vkCmdEndRenderPass ( drawCmdBuffers [ i ] ) ;
VK_CHECK_RESULT ( vkEndCommandBuffer ( drawCmdBuffers [ i ] ) ) ;
}
}
void loadAssets ( )
{
const uint32_t glTFLoadingFlags = vkglTF : : FileLoadingFlags : : PreTransformVertices | vkglTF : : FileLoadingFlags : : PreMultiplyVertexColors | vkglTF : : FileLoadingFlags : : FlipY ;
2022-04-21 07:34:44 +02:00
scene . loadFromFile ( getAssetPath ( ) + " models/color_teapot_spheres.gltf " , vulkanDevice , queue , glTFLoadingFlags ) ;
2022-04-03 18:22:53 +02:00
}
void setupDescriptorPool ( )
{
2022-04-21 07:34:44 +02:00
std : : vector < VkDescriptorPoolSize > poolSizes = {
2022-04-03 18:22:53 +02:00
vks : : initializers : : descriptorPoolSize ( VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 1 )
} ;
2022-04-21 07:34:44 +02:00
VkDescriptorPoolCreateInfo descriptorPoolInfo = vks : : initializers : : descriptorPoolCreateInfo ( poolSizes , 2 ) ;
2022-04-03 18:22:53 +02:00
VK_CHECK_RESULT ( vkCreateDescriptorPool ( device , & descriptorPoolInfo , nullptr , & descriptorPool ) ) ;
}
void setupDescriptorSetLayout ( )
{
2022-04-21 07:34:44 +02:00
std : : vector < VkDescriptorSetLayoutBinding > setLayoutBindings = {
vks : : initializers : : descriptorSetLayoutBinding ( VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , VK_SHADER_STAGE_VERTEX_BIT , 0 )
2022-04-03 18:22:53 +02:00
} ;
2022-04-21 07:34:44 +02:00
VkDescriptorSetLayoutCreateInfo descriptorLayout = vks : : initializers : : descriptorSetLayoutCreateInfo ( setLayoutBindings ) ;
2022-04-03 18:22:53 +02:00
VK_CHECK_RESULT ( vkCreateDescriptorSetLayout ( device , & descriptorLayout , nullptr , & descriptorSetLayout ) ) ;
2022-04-21 07:34:44 +02:00
VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = vks : : initializers : : pipelineLayoutCreateInfo ( & descriptorSetLayout , 1 ) ;
2022-04-03 18:22:53 +02:00
VK_CHECK_RESULT ( vkCreatePipelineLayout ( device , & pPipelineLayoutCreateInfo , nullptr , & pipelineLayout ) ) ;
}
void setupDescriptorSet ( )
{
2022-04-21 07:34:44 +02:00
VkDescriptorSetAllocateInfo allocInfo = vks : : initializers : : descriptorSetAllocateInfo ( descriptorPool , & descriptorSetLayout , 1 ) ;
2022-04-03 18:22:53 +02:00
VK_CHECK_RESULT ( vkAllocateDescriptorSets ( device , & allocInfo , & descriptorSet ) ) ;
2022-04-21 07:34:44 +02:00
std : : vector < VkWriteDescriptorSet > writeDescriptorSets = {
vks : : initializers : : writeDescriptorSet ( descriptorSet , VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 0 , & uniformBuffer . descriptor )
2022-04-03 18:22:53 +02:00
} ;
2022-04-21 07:34:44 +02:00
vkUpdateDescriptorSets ( device , writeDescriptorSets . size ( ) , writeDescriptorSets . data ( ) , 0 , nullptr ) ;
2022-04-03 18:22:53 +02:00
}
2022-04-04 09:58:46 +02:00
// 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
2022-04-21 07:34:44 +02:00
bool loadShaderFile ( std : : string fileName , ShaderInfo & shaderInfo )
2022-04-04 09:58:46 +02:00
{
2022-04-21 07:34:44 +02:00
# if defined(__ANDROID__)
2022-04-04 09:58:46 +02:00
// Load shader from compressed asset
2022-04-21 07:34:44 +02:00
// @todo
AAsset * asset = AAssetManager_open ( androidApp - > activity - > assetManager , fileName , AASSET_MODE_STREAMING ) ;
2022-04-04 09:58:46 +02:00
assert ( asset ) ;
size_t size = AAsset_getLength ( asset ) ;
assert ( size > 0 ) ;
2022-04-21 07:34:44 +02:00
shaderInfo . size = size ;
shaderInfo . code = new uint32_t [ size / 4 ] ;
2022-04-04 09:58:46 +02:00
AAsset_read ( asset , shaderCode , size ) ;
AAsset_close ( asset ) ;
# else
std : : ifstream is ( fileName , std : : ios : : binary | std : : ios : : in | std : : ios : : ate ) ;
if ( is . is_open ( ) )
{
shaderInfo . size = is . tellg ( ) ;
is . seekg ( 0 , std : : ios : : beg ) ;
shaderInfo . code = new uint32_t [ shaderInfo . size ] ;
is . read ( reinterpret_cast < char * > ( shaderInfo . code ) , shaderInfo . size ) ;
is . close ( ) ;
return true ;
} else {
std : : cerr < < " Error: Could not open shader file \" " < < fileName < < " \" " < < " \n " ;
2022-04-21 07:34:44 +02:00
throw std : : runtime_error ( " Could open shader file " ) ;
2022-04-04 09:58:46 +02:00
return false ;
}
# endif
2022-04-21 07:34:44 +02:00
}
2022-04-04 09:58:46 +02:00
2022-04-21 07:34:44 +02:00
// Create the shared pipeline parts up-front
void preparePipelineLibrary ( )
2022-04-03 20:15:50 +02:00
{
2022-04-21 07:34:44 +02:00
// 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 ;
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 ) ;
2022-07-30 09:14:11 +02:00
VkGraphicsPipelineCreateInfo pipelineLibraryCI { } ;
pipelineLibraryCI . sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO ;
pipelineLibraryCI . flags = VK_PIPELINE_CREATE_LIBRARY_BIT_KHR | VK_PIPELINE_CREATE_RETAIN_LINK_TIME_OPTIMIZATION_INFO_BIT_EXT ;
pipelineLibraryCI . sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO ;
pipelineLibraryCI . pNext = & libraryInfo ;
pipelineLibraryCI . pInputAssemblyState = & inputAssemblyState ;
pipelineLibraryCI . pVertexInputState = & vertexInputState ;
VK_CHECK_RESULT ( vkCreateGraphicsPipelines ( device , pipelineCache , 1 , & pipelineLibraryCI , nullptr , & pipelineLibrary . vertexInputInterface ) ) ;
2022-04-21 07:34:44 +02:00
}
2022-04-03 20:15:50 +02:00
2022-04-21 07:34:44 +02:00
// 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 ;
VkDynamicState vertexDynamicStates [ 2 ] = {
VK_DYNAMIC_STATE_VIEWPORT ,
VK_DYNAMIC_STATE_SCISSOR } ;
VkPipelineDynamicStateCreateInfo dynamicInfo { } ;
dynamicInfo . sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO ;
dynamicInfo . dynamicStateCount = 2 ;
dynamicInfo . pDynamicStates = vertexDynamicStates ;
VkPipelineViewportStateCreateInfo viewportState = { } ;
viewportState . sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO ;
viewportState . viewportCount = 1 ;
viewportState . scissorCount = 1 ;
VkPipelineRasterizationStateCreateInfo rasterizationState = vks : : initializers : : pipelineRasterizationStateCreateInfo ( VK_POLYGON_MODE_FILL , VK_CULL_MODE_BACK_BIT , VK_FRONT_FACE_COUNTER_CLOCKWISE , 0 ) ;
// @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 " ;
2022-07-30 09:14:11 +02:00
VkGraphicsPipelineCreateInfo pipelineLibraryCI { } ;
pipelineLibraryCI . sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO ;
pipelineLibraryCI . pNext = & libraryInfo ;
pipelineLibraryCI . renderPass = renderPass ;
pipelineLibraryCI . flags = VK_PIPELINE_CREATE_LIBRARY_BIT_KHR | VK_PIPELINE_CREATE_RETAIN_LINK_TIME_OPTIMIZATION_INFO_BIT_EXT ;
pipelineLibraryCI . stageCount = 1 ;
pipelineLibraryCI . pStages = & shaderStageCI ;
pipelineLibraryCI . layout = pipelineLayout ;
pipelineLibraryCI . pDynamicState = & dynamicInfo ;
pipelineLibraryCI . pViewportState = & viewportState ;
pipelineLibraryCI . pRasterizationState = & rasterizationState ;
VK_CHECK_RESULT ( vkCreateGraphicsPipelines ( device , pipelineCache , 1 , & pipelineLibraryCI , nullptr , & pipelineLibrary . preRasterizationShaders ) ) ;
2022-04-21 07:34:44 +02:00
}
2022-04-04 10:22:54 +02:00
2022-04-21 07:34:44 +02:00
// 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 ;
2022-07-30 09:14:11 +02:00
pipelineLibraryCI . flags = VK_PIPELINE_CREATE_LIBRARY_BIT_KHR | VK_PIPELINE_CREATE_RETAIN_LINK_TIME_OPTIMIZATION_INFO_BIT_EXT ;
2022-04-21 07:34:44 +02:00
pipelineLibraryCI . pColorBlendState = & colorBlendState ;
pipelineLibraryCI . pMultisampleState = & multisampleState ;
VK_CHECK_RESULT ( vkCreateGraphicsPipelines ( device , pipelineCache , 1 , & pipelineLibraryCI , nullptr , & pipelineLibrary . fragmentOutputInterface ) ) ;
}
2022-04-03 20:15:50 +02:00
}
2022-04-21 07:34:44 +02:00
void threadFn ( )
2022-04-03 18:22:53 +02:00
{
2022-04-21 07:34:44 +02:00
const std : : lock_guard < std : : mutex > lock ( mutex ) ;
2022-04-03 18:22:53 +02:00
2022-04-21 07:34:44 +02:00
auto start = std : : chrono : : steady_clock : : now ( ) ;
2022-04-03 18:22:53 +02:00
2022-04-21 07:34:44 +02:00
prepareNewPipeline ( ) ;
newPipelineCreated = true ;
2022-04-03 18:22:53 +02:00
2022-04-21 07:34:44 +02:00
// Change viewport/draw count
if ( pipelines . size ( ) > splitX * splitY ) {
splitX + + ;
splitY + + ;
}
2022-04-04 09:58:46 +02:00
2022-04-21 07:34:44 +02:00
auto delta = std : : chrono : : duration_cast < std : : chrono : : microseconds > ( std : : chrono : : steady_clock : : now ( ) - start ) ;
std : : cout < < " Pipeline created in " < < delta . count ( ) < < " microseconds \n " ;
2022-04-03 18:22:53 +02:00
}
2022-04-21 07:34:44 +02:00
// Create a new pipeline using the pipeline library and a customized fragment shader
// Used from a thread
void prepareNewPipeline ( )
2022-04-03 18:22:53 +02:00
{
2022-04-21 07:34:44 +02:00
// Create the fragment shader part of the pipeline library with some random options
2022-04-03 18:22:53 +02:00
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 ) ;
2022-04-21 07:34:44 +02:00
VkPipelineMultisampleStateCreateInfo multisampleState = vks : : initializers : : pipelineMultisampleStateCreateInfo ( VK_SAMPLE_COUNT_1_BIT ) ;
2022-04-03 18:22:53 +02:00
2022-04-21 07:34:44 +02:00
// 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 ) ;
2022-04-04 09:58:46 +02:00
2022-04-21 07:34:44 +02:00
VkShaderModuleCreateInfo shaderModuleCI { } ;
shaderModuleCI . sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO ;
shaderModuleCI . codeSize = shaderInfo . size ;
shaderModuleCI . pCode = shaderInfo . code ;
2022-04-04 09:58:46 +02:00
2022-04-21 07:34:44 +02:00
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 " ;
2022-04-04 10:22:54 +02:00
2022-04-21 07:34:44 +02:00
// Select lighting model using a specialization constant
srand ( ( unsigned int ) time ( NULL ) ) ;
uint32_t lighting_model = ( int ) ( rand ( ) % 4 ) ;
2022-04-03 20:15:50 +02:00
2022-04-21 07:34:44 +02:00
// 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 ;
2022-04-03 20:15:50 +02:00
2022-04-21 07:34:44 +02:00
shaderStageCI . pSpecializationInfo = & specializationInfo ;
2022-04-03 20:15:50 +02:00
2022-04-04 10:22:54 +02:00
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 ;
2022-04-21 07:34:44 +02:00
pipelineCI . stageCount = 1 ;
pipelineCI . pStages = & shaderStageCI ;
pipelineCI . layout = pipelineLayout ;
2022-04-04 10:22:54 +02:00
pipelineCI . renderPass = renderPass ;
2022-04-21 07:34:44 +02:00
pipelineCI . pDepthStencilState = & depthStencilState ;
2022-04-04 10:22:54 +02:00
pipelineCI . pMultisampleState = & multisampleState ;
2022-07-30 09:14:11 +02:00
VkPipeline fragmentShader = VK_NULL_HANDLE ;
VK_CHECK_RESULT ( vkCreateGraphicsPipelines ( device , threadPipelineCache , 1 , & pipelineCI , nullptr , & fragmentShader ) ) ;
2022-04-04 10:22:54 +02:00
2022-04-21 07:34:44 +02:00
// 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
2022-04-03 18:22:53 +02:00
std : : vector < VkPipeline > libraries = {
2022-04-21 07:34:44 +02:00
pipelineLibrary . vertexInputInterface ,
pipelineLibrary . preRasterizationShaders ,
2022-07-30 09:14:11 +02:00
fragmentShader ,
2022-04-21 07:34:44 +02:00
pipelineLibrary . fragmentOutputInterface } ;
// Link the library parts into a graphics pipeline
VkPipelineLibraryCreateInfoKHR pipelineLibraryCI { } ;
pipelineLibraryCI . sType = VK_STRUCTURE_TYPE_PIPELINE_LIBRARY_CREATE_INFO_KHR ;
pipelineLibraryCI . libraryCount = static_cast < uint32_t > ( libraries . size ( ) ) ;
pipelineLibraryCI . pLibraries = libraries . data ( ) ;
// 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 executablePipelineCI { } ;
executablePipelineCI . sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO ;
executablePipelineCI . pNext = & pipelineLibraryCI ;
2022-07-30 09:14:11 +02:00
executablePipelineCI . layout = pipelineLayout ;
if ( linkTimeOptimization )
{
// If link time optimization is activated in the UI, we set the VK_PIPELINE_CREATE_LINK_TIME_OPTIMIZATION_BIT_EXT flag which will let the implementation do additional optimizations at link time
// This trades in pipeline creation time for run-time performance
executablePipelineCI . flags = VK_PIPELINE_CREATE_LINK_TIME_OPTIMIZATION_BIT_EXT ;
}
2022-04-03 18:22:53 +02:00
2022-04-21 07:34:44 +02:00
VkPipeline executable = VK_NULL_HANDLE ;
VK_CHECK_RESULT ( vkCreateGraphicsPipelines ( device , threadPipelineCache , 1 , & executablePipelineCI , nullptr , & executable ) ) ;
2022-04-03 18:22:53 +02:00
2022-04-21 07:34:44 +02:00
pipelines . push_back ( executable ) ;
2022-07-30 09:14:11 +02:00
// Push fragment shader to list for deletion in the sample's destructor
pipelineLibrary . fragmentShaders . push_back ( fragmentShader ) ;
2022-04-03 18:22:53 +02:00
}
// Prepare and initialize uniform buffer containing shader uniforms
void prepareUniformBuffers ( )
{
// Create the vertex shader uniform buffer block
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 ( uboVS ) ) ) ;
// Map persistent
VK_CHECK_RESULT ( uniformBuffer . map ( ) ) ;
updateUniformBuffers ( ) ;
}
void updateUniformBuffers ( )
{
2022-04-21 07:34:44 +02:00
if ( ! paused ) {
rotation + = frameTimer * 0.1f ;
}
camera . setPerspective ( 45.0f , ( ( float ) width / ( float ) splitX ) / ( ( float ) height / ( float ) splitY ) , 0.1f , 256.0f ) ;
2022-04-03 18:22:53 +02:00
uboVS . projection = camera . matrices . perspective ;
2022-04-21 07:34:44 +02:00
uboVS . modelView = camera . matrices . view * glm : : rotate ( glm : : mat4 ( 1.0f ) , glm : : radians ( rotation * 360.0f ) , glm : : vec3 ( 0.0f , 1.0f , 0.0f ) ) ;
2022-04-03 18:22:53 +02:00
memcpy ( uniformBuffer . mapped , & uboVS , sizeof ( uboVS ) ) ;
}
void draw ( )
{
VulkanExampleBase : : prepareFrame ( ) ;
submitInfo . commandBufferCount = 1 ;
submitInfo . pCommandBuffers = & drawCmdBuffers [ currentBuffer ] ;
VK_CHECK_RESULT ( vkQueueSubmit ( queue , 1 , & submitInfo , VK_NULL_HANDLE ) ) ;
VulkanExampleBase : : submitFrame ( ) ;
}
void prepare ( )
{
VulkanExampleBase : : prepare ( ) ;
loadAssets ( ) ;
prepareUniformBuffers ( ) ;
setupDescriptorSetLayout ( ) ;
2022-04-21 07:34:44 +02:00
preparePipelineLibrary ( ) ;
2022-04-03 18:22:53 +02:00
setupDescriptorPool ( ) ;
setupDescriptorSet ( ) ;
buildCommandBuffers ( ) ;
2022-04-21 07:34:44 +02:00
// 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 ( ) ;
2022-04-03 18:22:53 +02:00
prepared = true ;
}
virtual void render ( )
{
if ( ! prepared )
return ;
2022-04-21 07:34:44 +02:00
if ( newPipelineCreated )
{
newPipelineCreated = false ;
vkQueueWaitIdle ( queue ) ;
buildCommandBuffers ( ) ;
2022-04-03 18:22:53 +02:00
}
2022-04-21 07:34:44 +02:00
draw ( ) ;
updateUniformBuffers ( ) ;
2022-04-03 18:22:53 +02:00
}
virtual void OnUpdateUIOverlay ( vks : : UIOverlay * overlay )
{
2022-07-30 09:14:11 +02:00
overlay - > checkBox ( " Link time optimization " , & linkTimeOptimization ) ;
2022-04-21 07:34:44 +02:00
if ( overlay - > button ( " New pipeline " ) ) {
// Spwan a thread to create a new pipeline in the background
std : : thread pipelineGenerationThread ( & VulkanExample : : threadFn , this ) ;
pipelineGenerationThread . detach ( ) ;
2022-04-03 18:22:53 +02:00
}
}
} ;
VULKAN_EXAMPLE_MAIN ( )