Vulkan pipeline cache configuration for first-launch shader compile stutter
0 reputation · 10 Mar 2021, 17:40 UTC
0 reputation · 10 Mar 2021, 17:40 UTC
An application targeting Vulkan 1.3 builds its graphics pipelines through vkCreateGraphicsPipelines and wants to reduce the frame-time spikes that shader compilation causes on first launch. The intended configuration backs pipeline creation with a VkPipelineCache whose contents are persisted between runs.
Two constraints shape this setup. Pipeline cache blobs are opaque and not portable across drivers or hardware versions, so saved data cannot be reused blindly. Vulkan 1.3 also promotes pipeline creation cache control flags such as VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT and VK_PIPELINE_CREATE_EAGER_PIPELINE_COMPILE_BIT, yet drivers remain free to compile pipelines lazily or eagerly, and pre-1.3 devices may not expose these flags at all.
The unresolved decision is how a disk-backed cache should be combined with these creation flags, and whether a single cache object should serve every pipeline creation or be split per pipeline family.
VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT, so a cache miss is deferred instead of blocking frame delivery?pInitialData?VkPipelineCache across all pipelines change hit rates compared with separate caches per pipeline family?32275 reputation · 10 Mar 2021, 21:27 UTC
Yes. Under Vulkan 1.3 it is spec-legal to create a VkPipelineCache from a disk blob loaded in a previous run and use that cache object for vkCreateGraphicsPipelines with VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT set. If the cache cannot satisfy the pipeline without compilation, the call can return VK_ERROR_PIPELINE_COMPILE_REQUIRED instead of blocking the thread for shader compilation. That lets you defer the compile or fall back to a simpler path for that frame.
Cache validation is required before reuse. A single VkPipelineCache can be shared across all pipeline families; the spec imposes no restriction. Empirically a shared cache tends to increase reuse of compiled shader stages, while per-family caches reduce size and fragmentation. The choice should be driven by profiling on your target hardware.
Confirmed: Vulkan 1.3 core promotes VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT and VK_PIPELINE_CREATE_EAGER_PIPELINE_COMPILE_BIT. vkGetPipelineCacheHeaderVersion and vkGetPipelineCacheHeaderData are core in 1.3 and return device UUID, vendor ID, driver version and a pipeline cache UUID that changes with driver updates. Pipeline cache blobs are opaque and not portable across drivers or hardware versions.
Likely / driver dependent: VK_PIPELINE_CREATE_EAGER_PIPELINE_COMPILE_BIT is advisory. Drivers may still compile lazily. Combining it with FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT does not guarantee eager compilation, it only gives you a non-blocking error path when a compile would be required. Pre-1.3 devices expose the same flags via VK_KHR_pipeline_cache_control when available, but older drivers may ignore them.
Verification you can run now: load a cached blob, validate header fields against vkGetDeviceUUID and vkGetPhysicalDeviceProperties, create a pipeline with FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT and confirm the return code on a cache miss, measure first-frame time with and without the flag, and profile hit rate with a single shared cache versus per-family caches.
One missing detail that changes the recommendation: do you need to support devices that only expose VK_KHR_pipeline_cache_control and not Vulkan 1.3 core? If yes, flag availability must be queried at runtime and fallback handling for drivers that ignore the flags is required.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.