-
Notifications
You must be signed in to change notification settings - Fork 165
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Ways To Provide SPIR-V Chapter (#270)
* Add Ways To Provide SPIR-V Chapter * Apply feedback
- Loading branch information
1 parent
027f972
commit 2a98e56
Showing
3 changed files
with
157 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
// Copyright 2024 The Khronos Group, Inc. | ||
// SPDX-License-Identifier: CC-BY-4.0 | ||
|
||
ifndef::chapters[:chapters:] | ||
ifndef::images[:images: images/] | ||
|
||
[[ways-to-provide-spirv]] | ||
= Ways to Provide SPIR-V | ||
|
||
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. | ||
|
||
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 | ||
|
||
== vkCreateShaderModule | ||
|
||
In Vulkan 1.0 release, this was the only method there was to pass SPIR-V into Vulkan. It is a very simple workflow: | ||
|
||
[source,cpp] | ||
---- | ||
VkShaderModuleCreateInfo shader_module_ci; | ||
shader_module_ci.pCode = spirv_source; | ||
shader_module_ci.codeSize = sizeof(spirv_source); | ||
VkShaderModule shader_module; | ||
vkCreateShaderModule(device, &shader_module_ci, NULL, &shader_module); | ||
// used to create VkPipeline | ||
VkPipelineShaderStageCreateInfo pipeline_stage_ci; | ||
pipeline_stage_ci.module = shader_module; | ||
---- | ||
|
||
The most common issue for tools or drivers consuming `vkCreateShaderModule` is there is still information missing: | ||
|
||
* For graphics pipeline, what are the descriptor set layouts bound | ||
* What are the other shader stages that will be linked up to this `VkShaderModule` | ||
* If there are multiple entry-points, which one is set (via `VkPipelineShaderStageCreateInfo::pName`) | ||
* What are the final Specialization Constant values | ||
|
||
== Inlining inside VkPipelineShaderStageCreateInfo | ||
|
||
If you have support for either `VK_KHR_maintenance5` or `VK_EXT_graphics_pipeline_library` you can just skip the `VkShaderModule` object completely. | ||
|
||
When creating the `VkPipeline` you can just pass in SPIR-V then: | ||
|
||
[source,cpp] | ||
---- | ||
VkShaderModuleCreateInfo shader_module_ci; | ||
shader_module_ci.pCode = spirv_source; | ||
shader_module_ci.codeSize = sizeof(spirv_source); | ||
VkPipelineShaderStageCreateInfo pipeline_stage_ci; | ||
pipeline_stage_ci.pNext = &shader_module_ci | ||
pipeline_stage_ci.module = VK_NULL_HANDLE; | ||
---- | ||
|
||
== Shader Module Identifier | ||
|
||
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. | ||
|
||
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 | ||
|
||
The following is an example of the workflow an application will use: | ||
|
||
|
||
[source,cpp] | ||
---- | ||
// First time running an application | ||
VkShaderModule shader_module; | ||
vkCreateShaderModule(device, &shader_module_ci, NULL, &shader_module); | ||
VkShaderModuleIdentifierEXT sm_identifier; | ||
vkGetShaderModuleIdentifierEXT(device, shader_module, &sm_identifier); | ||
SaveToDisk(sm_identifier); | ||
// ----------- | ||
// Potentially a 2nd seperate application run | ||
// ----------- | ||
VkShaderModuleIdentifierEXT sm_identifier; | ||
LoadFromDisk(sm_identifier); | ||
VkPipelineShaderStageModuleIdentifierCreateInfoEXT shader_module_id_ci; | ||
shader_module_id_ci.identifierSize = sm_identifier.identifierSize; | ||
shader_module_id_ci.pIdentifier = sm_identifier.identifier; | ||
// Inline when creating the pipeline | ||
VkPipelineShaderStageCreateInfo pipeline_stage_ci; | ||
pipeline_stage_ci.pNext = &shader_module_id_ci | ||
pipeline_stage_ci.module = VK_NULL_HANDLE; | ||
---- | ||
|
||
== Graphics Pipeline Library | ||
|
||
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. | ||
|
||
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: | ||
|
||
[source,cpp] | ||
---- | ||
// Will be no SPIR-V | ||
VkPipeline vertex_input_lib; | ||
vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, create_info, NULL, vertex_input_lib); | ||
VkPipeline fragment_output_lib; | ||
vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, create_info, NULL, fragment_output_lib); | ||
// Will be SPIR-V | ||
VkPipeline pre_raster_lib; | ||
vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, create_info, NULL, pre_raster_lib); | ||
// May be SPIR-V (can have pipelines without fragment shaders) | ||
VkPipeline fragment_shader_lib; | ||
vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, create_info, NULL, fragment_shader_lib); | ||
// Will be no SPIR-V when linking in pipeline library containing the SPIR-V already | ||
VkPipeline executable_pipeline; | ||
VkPipelineLibraryCreateInfoKHR library_ci; | ||
library_ci.pLibraries = [vertex_input_lib, pre_raster_lib, fragment_shader_lib, fragment_output_lib]; | ||
create_info.pNext = &library_ci; | ||
vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, create_info, NULL, executable_pipeline); | ||
---- | ||
|
||
== Shader Objects | ||
|
||
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. | ||
|
||
|
||
[source,cpp] | ||
---- | ||
VkShaderCreateInfoEXT shader_ci; | ||
// Note that the SPIR-V can actually be passed in a binary blob | ||
shader_ci.codeType = VK_SHADER_CODE_TYPE_SPIRV_EXT; | ||
// Note that this is a void pointer unlike the uint32_t pointer found in VkShaderModuleCreateInfo | ||
shader_ci.pCode = spirv_source; | ||
shader_ci.codeSize = sizeof(spirv_source); | ||
VkShaderEXT shader_object; | ||
vkCreateShadersEXT(device, 1, &shader_ci, NULL, &shader_object); | ||
---- | ||
|
||
== vkCreateRayTracingPipelinesKHR::deferredOperation | ||
|
||
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. | ||
|
||
The link:https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#deferred-host-operations-requesting[spec states] | ||
|
||
> 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. | ||
|
||
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters