|
| 1 | +// Copyright 2024 The Khronos Group, Inc. |
| 2 | +// SPDX-License-Identifier: CC-BY-4.0 |
| 3 | + |
| 4 | +ifndef::chapters[:chapters:] |
| 5 | +ifndef::images[:images: images/] |
| 6 | + |
| 7 | +[[ways-to-provide-spirv]] |
| 8 | += Ways to Provide SPIR-V |
| 9 | + |
| 10 | +This chapter is designed for anyone who wants to write a tool to inspect, consume, edit, or do anything related to the SPIR-V modules passed into Vulkan. |
| 11 | + |
| 12 | +Over the years, more and more ways have been created to pass SPIR-V down to the driver and this chapter goes over them all |
| 13 | + |
| 14 | +== vkCreateShaderModule |
| 15 | + |
| 16 | +In Vulkan 1.0 release, this was the only method there was to pass SPIR-V into Vulkan. It is a very simple workflow: |
| 17 | + |
| 18 | +[source,cpp] |
| 19 | +---- |
| 20 | +VkShaderModuleCreateInfo shader_module_ci; |
| 21 | +shader_module_ci.pCode = spirv_source; |
| 22 | +shader_module_ci.codeSize = sizeof(spirv_source); |
| 23 | +
|
| 24 | +VkShaderModule shader_module; |
| 25 | +vkCreateShaderModule(device, &shader_module_ci, NULL, &shader_module); |
| 26 | +
|
| 27 | +// used to create VkPipeline |
| 28 | +VkPipelineShaderStageCreateInfo pipeline_stage_ci; |
| 29 | +pipeline_stage_ci.module = shader_module; |
| 30 | +---- |
| 31 | + |
| 32 | +The most common issue for tools or drivers consuming `vkCreateShaderModule` is there is still information missing: |
| 33 | + |
| 34 | +* For graphics pipeline, what are the descriptor set layouts bound |
| 35 | +* What are the other shader stages that will be linked up to this `VkShaderModule` |
| 36 | +* If there are multiple entry-points, which one is set (via `VkPipelineShaderStageCreateInfo::pName`) |
| 37 | +* What are the final Specialization Constant values |
| 38 | + |
| 39 | +== Inlining inside VkPipelineShaderStageCreateInfo |
| 40 | + |
| 41 | +If you have support for either `VK_KHR_maintenance5` or `VK_EXT_graphics_pipeline_library` you can just skip the `VkShaderModule` object completely. |
| 42 | + |
| 43 | +When creating the `VkPipeline` you can just pass in SPIR-V then: |
| 44 | + |
| 45 | +[source,cpp] |
| 46 | +---- |
| 47 | +VkShaderModuleCreateInfo shader_module_ci; |
| 48 | +shader_module_ci.pCode = spirv_source; |
| 49 | +shader_module_ci.codeSize = sizeof(spirv_source); |
| 50 | +
|
| 51 | +VkPipelineShaderStageCreateInfo pipeline_stage_ci; |
| 52 | +pipeline_stage_ci.pNext = &shader_module_ci |
| 53 | +pipeline_stage_ci.module = VK_NULL_HANDLE; |
| 54 | +---- |
| 55 | + |
| 56 | +== Shader Module Identifier |
| 57 | + |
| 58 | +The `VK_EXT_shader_module_identifier` extension allows the app to not even need the SPIR-V source anymore, instead the `VkShaderModule` can be cached on the disk and then in a future run of the application, a "ID" pointing to that cache can be used. |
| 59 | + |
| 60 | +The biggest challenge for tools is you don't have a chance to see the original `vkGetShaderModuleIdentifierEXT` call, there will be no chance to know what is inside the SPIR-V |
| 61 | + |
| 62 | +The following is an example of the workflow an application will use: |
| 63 | + |
| 64 | + |
| 65 | +[source,cpp] |
| 66 | +---- |
| 67 | +// First time running an application |
| 68 | +VkShaderModule shader_module; |
| 69 | +vkCreateShaderModule(device, &shader_module_ci, NULL, &shader_module); |
| 70 | +
|
| 71 | +VkShaderModuleIdentifierEXT sm_identifier; |
| 72 | +vkGetShaderModuleIdentifierEXT(device, shader_module, &sm_identifier); |
| 73 | +
|
| 74 | +SaveToDisk(sm_identifier); |
| 75 | +
|
| 76 | +// ----------- |
| 77 | +// Potentially a 2nd seperate application run |
| 78 | +// ----------- |
| 79 | +
|
| 80 | +VkShaderModuleIdentifierEXT sm_identifier; |
| 81 | +LoadFromDisk(sm_identifier); |
| 82 | +
|
| 83 | +VkPipelineShaderStageModuleIdentifierCreateInfoEXT shader_module_id_ci; |
| 84 | +shader_module_id_ci.identifierSize = sm_identifier.identifierSize; |
| 85 | +shader_module_id_ci.pIdentifier = sm_identifier.identifier; |
| 86 | +
|
| 87 | +// Inline when creating the pipeline |
| 88 | +VkPipelineShaderStageCreateInfo pipeline_stage_ci; |
| 89 | +pipeline_stage_ci.pNext = &shader_module_id_ci |
| 90 | +pipeline_stage_ci.module = VK_NULL_HANDLE; |
| 91 | +---- |
| 92 | + |
| 93 | +== Graphics Pipeline Library |
| 94 | + |
| 95 | +The `VK_EXT_graphics_pipeline_library` extension breaks up the pipeline into 4 smaller parts with the intent of allowing faster pipeline loading for applications reusing the same shaders or state in multiple pipelines. |
| 96 | + |
| 97 | +From a tools point a view, you may see up to 5 different `vkCreateGraphicsPipelines` calls, but only 2 of them will have SPIR-V in it. The following is an example workflow: |
| 98 | + |
| 99 | +[source,cpp] |
| 100 | +---- |
| 101 | +// Will be no SPIR-V |
| 102 | +VkPipeline vertex_input_lib; |
| 103 | +vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, create_info, NULL, vertex_input_lib); |
| 104 | +VkPipeline fragment_output_lib; |
| 105 | +vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, create_info, NULL, fragment_output_lib); |
| 106 | +
|
| 107 | +// Will be SPIR-V |
| 108 | +VkPipeline pre_raster_lib; |
| 109 | +vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, create_info, NULL, pre_raster_lib); |
| 110 | +
|
| 111 | +// May be SPIR-V (can have pipelines without fragment shaders) |
| 112 | +VkPipeline fragment_shader_lib; |
| 113 | +vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, create_info, NULL, fragment_shader_lib); |
| 114 | +
|
| 115 | +// Will be no SPIR-V when linking in pipeline library containing the SPIR-V already |
| 116 | +VkPipeline executable_pipeline; |
| 117 | +VkPipelineLibraryCreateInfoKHR library_ci; |
| 118 | +library_ci.pLibraries = [vertex_input_lib, pre_raster_lib, fragment_shader_lib, fragment_output_lib]; |
| 119 | +create_info.pNext = &library_ci; |
| 120 | +vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, create_info, NULL, executable_pipeline); |
| 121 | +---- |
| 122 | + |
| 123 | +== Shader Objects |
| 124 | + |
| 125 | +The `VK_EXT_shader_object` extension created a completely new flow to pass state information in the command buffer that doesn't involve pipelines. The following is the workflow to pass in the SPIR-V. |
| 126 | + |
| 127 | + |
| 128 | +[source,cpp] |
| 129 | +---- |
| 130 | +VkShaderCreateInfoEXT shader_ci; |
| 131 | +// Note that the SPIR-V can actually be passed in a binary blob |
| 132 | +shader_ci.codeType = VK_SHADER_CODE_TYPE_SPIRV_EXT; |
| 133 | +// Note that this is a void pointer unlike the uint32_t pointer found in VkShaderModuleCreateInfo |
| 134 | +shader_ci.pCode = spirv_source; |
| 135 | +shader_ci.codeSize = sizeof(spirv_source); |
| 136 | +
|
| 137 | +VkShaderEXT shader_object; |
| 138 | +vkCreateShadersEXT(device, 1, &shader_ci, NULL, &shader_object); |
| 139 | +---- |
| 140 | + |
| 141 | +== vkCreateRayTracingPipelinesKHR::deferredOperation |
| 142 | + |
| 143 | +When dealing with Ray Tracing pipelines, it is important to note that a `VkDeferredOperationKHR` handle might be used to defer the creation of the pipeline to unblock the CPU thread. |
| 144 | + |
| 145 | +The link:https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#deferred-host-operations-requesting[spec states] |
| 146 | + |
| 147 | +> Parameters to the command requesting a deferred operation may be accessed by the implementation at any time until the deferred operation enters the complete state. |
| 148 | + |
| 149 | +In this particular case this means that if your tool is touching the SPIR-V being passed in, **all** parameters passed down to `vkCreateRayTracingPipelinesKHR`, including pointers, shader modules, inlined SPIR-V... must live until operation completion. |
0 commit comments