本章适用于所有希望编写工具以检查、使用、编辑 SPIR-V 模块或执行其他与传入 Vulkan 的 SPIR-V 模块相关操作的开发者。多年来向驱动传递 SPIR-V 的方式不断增多本章将逐一介绍所有这些方式。vkCreateShaderModule在 Vulkan 1.0 版本中这是向 Vulkan 传递 SPIR-V 的唯一方式工作流程十分简单VkShaderModuleCreateInfo shader_module_ci {}; shader_module_ci.sType VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; shader_module_ci.pCode spirv_source; // SPIR-V源码uint32_t数组 shader_module_ci.codeSize sizeof(spirv_source); // 源码大小字节数 VkShaderModule shader_module; vkCreateShaderModule(device, shader_module_ci, NULL, shader_module); // 用于创建VkPipeline VkPipelineShaderStageCreateInfo pipeline_stage_ci {}; pipeline_stage_ci.sType VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; pipeline_stage_ci.module shader_module; // 关联创建的着色器模块对于使用vkCreateShaderModule的工具或驱动而言最常见的问题是仍缺少部分关键信息对于图形管线绑定的描述符集布局是什么。哪些其他着色器阶段会与此VkShaderModule链接。若存在多个入口点将设置哪一个通过VkPipelineShaderStageCreateInfo::pName指定。最终的专用常量Specialization Constant值是什么。内嵌到 VkPipelineShaderStageCreateInfo 中若支持VK_KHR_maintenance5或VK_EXT_graphics_pipeline_library扩展可完全跳过VkShaderModule对象。创建设备管线VkPipeline时可直接传入 SPIR-V 源码VkShaderModuleCreateInfo shader_module_ci {}; shader_module_ci.sType VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; shader_module_ci.pCode spirv_source; shader_module_ci.codeSize sizeof(spirv_source); VkPipelineShaderStageCreateInfo pipeline_stage_ci {}; pipeline_stage_ci.sType VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; pipeline_stage_ci.pNext shader_module_ci; // 内嵌着色器模块创建信息 pipeline_stage_ci.module VK_NULL_HANDLE; // 无需关联VkShaderModule着色器模块标识符VK_EXT_shader_module_identifier扩展允许应用程序不再依赖 SPIR-V 源码 —— 可将VkShaderModule缓存到磁盘后续应用程序运行时仅需使用指向该缓存的 “标识符ID” 即可。对于工具而言最大的挑战在于若无法捕获原始的vkGetShaderModuleIdentifierEXT调用将无法知晓 SPIR-V 内部的具体内容。以下是应用程序使用该方式的工作流程示例// 应用程序第一次运行 VkShaderModule shader_module; vkCreateShaderModule(device, shader_module_ci, NULL, shader_module); VkShaderModuleIdentifierEXT sm_identifier {}; vkGetShaderModuleIdentifierEXT(device, shader_module, sm_identifier); // 将标识符保存到磁盘 SaveToDisk(sm_identifier); // ----------- // 可能是应用程序的第二次独立运行 // ----------- VkShaderModuleIdentifierEXT sm_identifier; // 从磁盘加载标识符 LoadFromDisk(sm_identifier); VkPipelineShaderStageModuleIdentifierCreateInfoEXT shader_module_id_ci {}; shader_module_id_ci.sType VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_MODULE_IDENTIFIER_CREATE_INFO_EXT; shader_module_id_ci.identifierSize sm_identifier.identifierSize; shader_module_id_ci.pIdentifier sm_identifier.identifier; // 创建设备管线时内嵌标识符 VkPipelineShaderStageCreateInfo pipeline_stage_ci {}; pipeline_stage_ci.sType VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; pipeline_stage_ci.pNext shader_module_id_ci; pipeline_stage_ci.module VK_NULL_HANDLE;图形管线库VK_EXT_graphics_pipeline_library扩展将管线拆分为 4 个更小的部分旨在让多次复用相同着色器或状态的应用程序能更快地加载管线。从工具的角度来看可能会看到最多 5 次vkCreateGraphicsPipelines调用但其中仅有 2 次会包含 SPIR-V 源码。以下是工作流程示例// 无SPIR-V源码仅包含顶点输入状态 VkPipeline vertex_input_lib; vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, vertex_input_create_info, NULL, vertex_input_lib); // 无SPIR-V源码仅包含片元输出状态 VkPipeline fragment_output_lib; vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, fragment_output_create_info, NULL, fragment_output_lib); // 包含SPIR-V源码包含光栅化前阶段着色器 VkPipeline pre_raster_lib; vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, pre_raster_create_info, NULL, pre_raster_lib); // 可能包含SPIR-V源码可包含片元着色器也可不含片元着色器 VkPipeline fragment_shader_lib; vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, fragment_shader_create_info, NULL, fragment_shader_lib); // 链接已包含SPIR-V的管线库时无SPIR-V源码 VkPipeline executable_pipeline; VkPipelineLibraryCreateInfoKHR library_ci {}; library_ci.sType VK_STRUCTURE_TYPE_PIPELINE_LIBRARY_CREATE_INFO_KHR; library_ci.libraryCount 4; library_ci.pLibraries [vertex_input_lib, pre_raster_lib, fragment_shader_lib, fragment_output_lib]; VkGraphicsPipelineCreateInfo create_info {}; create_info.sType VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; create_info.pNext library_ci; vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, create_info, NULL, executable_pipeline);着色器对象VK_EXT_shader_object扩展创建了一种全新的工作流程可在不涉及管线的情况下向命令缓冲区传递状态信息。以下是传递 SPIR-V 的工作流程VkShaderCreateInfoEXT shader_ci {}; shader_ci.sType VK_STRUCTURE_TYPE_SHADER_CREATE_INFO_EXT; shader_ci.codeType VK_SHADER_CODE_TYPE_SPIRV_EXT; // 代码类型为SPIR-V shader_ci.pCode spirv_source; // SPIR-V源码void指针不同于VkShaderModuleCreateInfo的uint32_t指针 shader_ci.codeSize sizeof(spirv_source); // 源码大小字节数 VkShaderEXT shader_object; vkCreateShadersEXT(device, 1, shader_ci, NULL, shader_object);vkCreateRayTracingPipelinesKHR::deferredOperation处理光线追踪管线时需注意可使用VkDeferredOperationKHR句柄延迟管线创建以避免阻塞 CPU 线程。Vulkan 规范规定请求延迟操作的命令参数在延迟操作进入完成状态前可能会被实现随时访问。具体到本场景这意味着若工具需操作传入的 SPIR-V那么传递给vkCreateRayTracingPipelinesKHR的所有参数包括指针、着色器模块、内嵌的 SPIR-V 等都必须在操作完成前保持有效。