VK_KHR_pipeline_binary.proposal
This extension proposes a method to directly retrieve binary data associated with individual pipelines, bypassing the pipeline caching mechanism, and enabling applications to manage caching themselves.
Problem Statement
Vulkan 1.0 introduced the concept of pipeline caches, which were designed to allow drivers to reuse blobs of state or shader code between different pipelines more explicitly. The original idea was that the driver would know best which parts of state could be reused, and applications only needed to manage storage and threading, making the interface fairly straightforward. Since then, developers and platforms have found use cases or corner cases which have shown deficiencies in the API, and in many cases have designed their own caching system on top of Vulkan.
To address these deficiencies, the Vulkan WG has released a number of extensions to change the behavior of caches, fixing issues as they come up. This has meant pipeline caches have become a very complex piece of software, and tweaking them is actually getting more difficult as time goes on. In many cases, we are seeing applications using their own caching mechanisms in ways that require them to actively "fight" Vulkan’s caching mechanism to try to get it to do what they want.
Solution Space
There are a two key possibilities for solving these issues:
- Continue providing additional functionality
- Enable applications to have more control over caching
The former approach will continue to show improvements in the ecosystem as new extensions show up in the wild, but it relies on drivers being continually updated for applications to take advantage of new features. It also means that pipeline caches continue to grow in complexity, exacerbating the problem as much as solving it.
Enabling applications to get more involved with caching could both allow applications to do the caching they want, while also reducing complexity if done carefully. Within this solution space, there are two main possibilities:
- Add partial access to the existing caching infrastructure
- e.g. via callbacks
- Provide direct access to pipeline binaries, bypassing the caching infrastructure
Either approach could work, but the concern with integrating into the existing caching infrastructure is that the infrastructure remains - there is no guarantee that we will not need to add more features in future to solve new problems. With the direct access approach it is slightly harder to express a multi-level caching strategy, but should still be doable.
The solution should allow an application to:
- Control memory usage such that e.g. an LRU pipeline cache with certain on-disk/memory bounds could be created.
- Interact with an internal driver cache directly in such a way as to be able to avoid potential micro-stutters due to disk I/O by doing driver cache look-ups ahead of time, rather than at CreatePipeline time.
- Control whether an internal driver cache exists, including on specialized platforms such as Steam that prepropulate driver caches.
- Deduplicate binaries when they are used in multiple pipelines.
- Create pipelines from binaries without the need to provide SPIR-V.
- Create a caching scheme that is no less efficient than the implementation of the Vulkan pipeline cache.
Proposal
This proposal allows applications to completely bypass pipeline caching, by obtaining key/data pairs for a pipeline, and allowing applications to manage these in their own caching infrastructure.
Pipeline binary objects encapsulate data from compiled pipelines, allowing the data to be stored by the application and used to recreate pipelines in the future, without the need for compilation.
A pipeline key can be queried using a Vk*PipelineCreateInfo
structure, which can then be used by the application to look up the required binary/binaries in its cache.
Pipeline binary objects can be created in three different ways:
- From
VkPipeline
objects that were created with theVK_PIPELINE_CREATE_2_CAPTURE_DATA_BIT_KHR
flag set. - From data blobs serialized from previous pipeline binary objects.
- By querying an implementation’s internal driver cache, using a
Vk*PipelineCreateInfo
structure.
A binary key and data blob can be queried for each binary object, allowing deduplication of binaries with identical keys and storing of the data in the application’s cache.
API Changes
Obtaining and Using Key/Data Pairs
VK_DEFINE_HANDLE(VkPipelineBinaryKHR)
const uint32_t VK_MAX_PIPELINE_BINARY_KEY_SIZE_KHR = 32;
typedef struct VkPipelineBinaryKeyKHR {
VkStructureType sType;
void* pNext;
uint32_t keySize;
uint8_t key[VK_MAX_PIPELINE_BINARY_KEY_SIZE_KHR];
} VkPipelineBinaryKeyKHR;
typedef struct VkPipelineBinaryDataKHR {
size_t dataSize;
void* pData;
} VkPipelineBinaryDataKHR;
typedef struct VkPipelineBinaryKeysAndDataKHR {
uint32_t binaryCount;
const VkPipelineBinaryKeyKHR* pPipelineBinaryKeys;
const VkPipelineBinaryDataKHR* pPipelineBinaryData;
} VkPipelineBinaryKeysAndDataKHR;
typedef struct VkPipelineCreateInfoKHR {
VkStructureType sType;
const void* pNext;
} VkPipelineCreateInfoKHR;
typedef struct VkPipelineBinaryCreateInfoKHR {
VkStructureType sType;
const void* pNext;
const VkPipelineBinaryKeysAndDataKHR* pKeysAndDataInfo;
VkPipeline pipeline;
const VkPipelineCreateInfoKHR* pPipelineCreateInfo;
} VkPipelineBinaryCreateInfoKHR;
typedef struct VkPipelineBinaryHandlesInfoKHR {
VkStructureType sType;
const void* pNext;
uint32_t pipelineBinaryCount;
VkPipelineBinaryKHR* pPipelineBinaries;
} VkPipelineBinaryHandlesInfoKHR;
VkResult vkCreatePipelineBinariesKHR(
VkDevice device,
const VkPipelineBinaryCreateInfoKHR* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkPipelineBinaryHandlesInfoKHR* pBinaries);
void vkDestroyPipelineBinaryKHR(
VkDevice device,
VkPipelineBinaryKHR pipelineBinary,
const VkAllocationCallbacks* pAllocator);
VkResult vkGetPipelineKeyKHR(
VkDevice device,
const VkPipelineCreateInfoKHR* pPipelineCreateInfo,
VkPipelineBinaryKeyKHR* pPipelineKey);
typedef struct VkPipelineBinaryDataInfoKHR {
VkStructureType sType;
const void* pNext;
VkPipelineBinaryKHR pipelineBinary;
} VkPipelineBinaryDataInfoKHR;
VkResult vkGetPipelineBinaryDataKHR(
VkDevice device,
const VkPipelineBinaryDataInfoKHR* pInfo,
VkPipelineBinaryKeyKHR* pPipelineBinaryKey,
size_t* pPipelineBinaryDataSize,
void* pPipelineBinaryData);
typedef struct VkReleaseCapturedPipelineDataInfoKHR {
VkStructureType sType;
const void* pNext;
VkPipeline pipeline;
} VkReleaseCapturedPipelineDataInfoKHR;
VkResult vkReleaseCapturedPipelineDataKHR(
VkDevice device,
const VkReleaseCapturedPipelineDataInfoKHR* pInfo,
const VkAllocationCallbacks* pAllocator);
vkGetPipelineKeyKHR
works on any existing pipeline creation info structure (via pNext
in VkPipelineCreateInfoKHR
), allowing an application to obtain a pipeline key before creating a pipeline.
This allows the application to use that key to internally lookup the pipeline binary keys and data previously obtained via vkGetPipelineBinaryDataKHR
, before creating the pipeline.
An implementation may return identical pipeline keys for different pipelines if the parts of the VkPipelineCreateInfoKHR
needed by the implementation to create binaries is identical.
Pipeline binary keys identify the contents of the pipeline binary object. Multiple pipelines may use the same binary, e.g. an implementation may generate identical binaries for two pipelines that have the same vertex shader, so the pipeline binary key can be used by the application as a unique identifier and to deduplicate binaries.
Setting pPipelineCreateInfo
to NULL
when calling vkGetPipelineKeyKHR
allows an application to query the implementation’s global key. This global
key can be compared on a subsequent run to determine if saved keys and binary data for pipelines remain valid.
Unlike most global keys in the API, which are exposed as various *UUID
physical-device queries,
the global pipeline key may depend on state which is only known at device creation time,
such as extensions and features being enabled on the device, or even enabled layers in some cases.
vkCreatePipelineBinariesKHR
can be used in 3 different ways to create VkPipelineBinaryKHR
objects:
- Setting
VkPipelineBinaryCreateInfoKHR.pipeline
allows an application to query the number of binaries for a pipeline and then create that number of binary objects from that pipeline. VkPipelineBinaryCreateInfoKHR.pKeysAndDataInfo
can be used to create binary objects from data previously retrieved usingvkGetPipelineBinaryDataKHR
.- The
pipelineBinaryInternalCache
property indicates that an application can useVkPipelineBinaryCreateInfoKHR.pPipelineCreateInfo
to see if the implementation has the pipeline binary stored in its internal cache. An application can query the number of binaries and then create that number of binary objects in a similar way to creating binaries from a pipeline object.
Only one of pipeline
, pKeysAndDataInfo
, and pPipelineCreateInfo
can be used at once.
A new VkResult value is added so that vkCreatePipelineBinariesKHR
can indicate that an implementation supporting pipelineBinaryInternalCache
xref::name::_properties does not have a binary in its internal cache:
VK_PIPELINE_BINARY_MISSING_KHR = 1000483000
A new VkResult value is added so that vkGetPipelineBinaryDataKHR
can indicate that the application has not provided enough storage to write pipeline binary data into:
VK_ERROR_NOT_ENOUGH_SPACE_KHR = -1000483000
A new VkPipelineCreateFlagBits2KHR value is required to be able to obtain binary data from a pipeline object via this extension after creation:
VK_PIPELINE_CREATE_2_CAPTURE_DATA_BIT_KHR = 0x80000000
Calling vkReleaseCapturedPipelineDataKHR
allows the implementation to free any resources captured as a result of creating the pipeline with VK_PIPELINE_CREATE_2_CAPTURE_DATA_BIT_KHR
and put the pipeline into a state as if VK_PIPELINE_CREATE_2_CAPTURE_DATA_BIT_KHR
had not been provided at pipeline creation time.
A new creation structure is also provided to pass in any key/data pairs the application has available:
typedef struct VkPipelineBinaryInfoKHR {
VkStructureType sType;
const void* pNext;
uint32_t binaryCount;
const VkPipelineBinaryKHR* pPipelineBinaries;
} VkPipelineBinaryInfoKHR;
It is the application’s responsibility to ensure the pipeline create info in this call exactly matches the pipeline create info of the pipeline used to create the key/binary pairs, other than the inclusion of this structure and any shader modules that were declared in VkPipelineShaderStageCreateInfo
instances at key generation time as they will be ignored by the implementation when creating a pipeline from binaries.
Note that when creating a pipeline from binaries binaryCount
in VkPipelineBinaryInfoKHR
and the value in pipelineBinaryCount
returned by vkCreatePipelineBinariesKHR
must be matching for a given pipeline/create info, and the order of the binaries in pPipelineBinaries
must match those returned by vkCreatePipelineBinariesKHR
.
Features
The following new features are exposed by the extension:
typedef struct VkPhysicalDevicePipelineBinaryFeaturesKHR {
VkStructureType sType;
void* pNext;
VkBool32 pipelineBinaries;
} VkPhysicalDevicePipelineBinaryFeaturesKHR;
pipelineBinaries
is the main feature enabling this extension’s functionality and must be supported if this extension is supported.
Properties
On some platforms, the internal pipeline cache is still very important and may be maintained outside the scope of the application. To avoid a situation where the application and implementation maintain duplicated entries of their pipeline caches, or worse, ignore all the work done to prepare the internal cache, there are properties which aim to expose this cache behavior to the application so that it can make an informed decision.
All these properties are mostly useful as hints to an application that may want to take advantage of them. It is valid for an application to ignore them.
typedef struct VkPhysicalDevicePipelineBinaryPropertiesKHR {
VkStructureType sType;
void* pNext;
VkBool32 pipelineBinaryInternalCache;
VkBool32 pipelineBinaryInternalCacheControl;
VkBool32 pipelineBinaryPrefersInternalCache;
VkBool32 pipelineBinaryPrecompiledInternalCache;
VkBool32 pipelineBinaryCompressedData;
} VkPhysicalDevicePipelineBinaryPropertiesKHR;
pipelineBinaryInternalCache
When pipelineBinaryInternalCache
is supported it is possible to create pipeline binaries using just the pipeline create info, without providing either SPIR-V or binary data, by
checking if the implementation has the pipeline binary stored in its internal cache.
VkGraphicsPipelineCreateInfo graphicsCreateInfo;
VkPipelineCreateInfoKHR pipelineCreateInfo;
pipelineCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CREATE_INFO_KHR;
pipelineCreateInfo.pNext = &graphicsCreateInfo;
VkPipelineBinaryCreateInfoKHR createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_BINARY_CREATE_INFO_KHR;
createInfo.pNext = NULL;
createInfo.pKeysAndDataInfo = NULL;
createInfo.pipeline = VK_NULL_HANDLE;
createInfo.pPipelineCreateInfo = &pipelineCreateInfo;
VkPipelineBinaryHandlesInfoKHR handlesInfo;
handlesInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_BINARY_HANDLES_INFO_KHR;
handlesInfo.pNext = NULL;
handlesInfo.pipelineBinaryCount = 0;
handlesInfo.pPipelineBinaries = NULL;
VkResult res = vkCreatePipelineBinariesKHR(device, &createInfo, NULL, &handlesInfo);
if (res == VK_PIPELINE_BINARY_MISSING_KHR) {
// Attempted to create a pipeline binary, but implementation does not have it in cache.
// Similar to VK_PIPELINE_COMPILE_REQUIRED, this is a positive return value.
return;
}
std::vector<VkPipelineBinaryKHR> pipelineBinaries;
pipelineBinaries.resize(handlesInfo.pipelineBinaryCount);
handlesInfo.pPipelineBinaries = pipelineBinaries.data();
vkCreatePipelineBinariesKHR(device, &createInfo, NULL, &handlesInfo);
While this mechanism looks very similar to VK_EXT_shader_module_identifier
shader creation,
the main rationale for doing it like this,
rather than supporting passing in VK_NULL_HANDLE
pipeline binary to pipeline creation is:
- Can query early if pipeline creation will succeed. Rather than having to accept arbitrary failure when compiling with identifiers only, this allows an application to pull in pipeline data for all known keys up early, and can then later decide to kick off compilation work as needed.
- Avoids potential disk I/O microstutter when creating a pipeline.
In the case of no handles being passed to pipeline creation, the implementation would have to do a last minute query into its
internal cache which would likely involve either locks and/or disk I/O,
neither which are desirable when doing last minute pipeline creation with for example
VK_EXT_graphics_pipeline_library
.
Just as with any internal cache, there are no guarantees that VK_PIPELINE_BINARY_MISSING_KHR
will not be returned.
It is considered a best-effort system.
When this property is not set, applications should assume that the implementation does not provide any on-disk caching on its own.
pipelineBinaryInternalCacheControl
When pipelineBinaryInternalCacheControl
is supported it is possible to disable the implementation’s
internal pipeline cache by adding the following structure to the pNext
chain of VkDeviceCreateInfo
when creating a device:
typedef struct VkDevicePipelineBinaryInternalCacheControlKHR {
VkStructureType sType;
const void* pNext;
VkBool32 disableInternalCache;
} VkDevicePipelineBinaryInternalCacheControlKHR;
If the disableInternalCache
is VK_TRUE
then the implementation’s internal cache is disabled,
allowing an application to take full control of both memory and disk usage.
When disableInternalCache
is VK_TRUE
, it is not allowed to attempt creating a VkPipelineBinaryKHR
without providing either SPIR-V or binary data.
pipelineBinaryPrefersInternalCache
If this is set, the implementations prefers that applications do not capture pipeline binaries themselves with VK_PIPELINE_CREATE_2_CAPTURE_DATA_BIT_KHR
and let the implementation manage the cache internally.
Rather, they can store pipeline keys or shader module identifiers instead, and aim to pull in binaries using the mechanism mentioned above.
An IHV implementation should not set this to VK_TRUE
in isolation.
The intention here is that a layer may decide to set this property to VK_TRUE
if the layer has knowledge that
the internal cache already exists on-disk, and is considered more important than the application’s cache.
pipelineBinaryPrecompiledInternalCache
If this is set, this is a hint to applications that pipelines may exist in the internal cache, despite the application never having observed a particular global pipeline key before. Creating pipeline binaries with the mechanism mentioned above may work, and applications are encouraged to try creating binaries from just pipeline creation infos.
This property is very similar to pipelineBinaryPrefersInternalCache
, in that IHV implementations are not expected to set this to VK_TRUE
,
unless they can prove there exists a precompiled cache somewhere. IHV implementations are not expected or supposed to provide this on their own,
but a specialized platform (e.g. a game console or embedded device) may decide to provide that.
The intention of this property is that a layer may have knowledge about such precompiled caches existing, and may override this value to VK_TRUE
.
pipelineBinaryCompressedData
If this is set, this is a hint to the application that the binary data is already compressed and the application should not perform any compression on it.
Examples
The following examples illustrate using an application defined cache to lookup binaries; any constraints or features of that caching system can be expressed within the application cache itself.
Retrieving the global key
// Get the global key
VkPipelineBinaryKeyKHR globalKey;
globalKey.sType = VK_STRUCTURE_TYPE_PIPELINE_BINARY_KEY_KHR;
vkGetPipelineKeyKHR(device, NULL, &globalKey);
// This can be used to ensure the app's cache is valid.
Retrieving the key for a PipelineCreateInfo
VkGraphicsPipelineCreateInfo graphicsCreateInfo;
// Get the pipeline key
VkPipelineCreateInfoKHR pipelineCreateInfo;
pipelineCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CREATE_INFO_KHR;
pipelineCreateInfo.pNext = &graphicsCreateInfo;
VkPipelineBinaryKeyKHR pipelineKey;
vkGetPipelineKeyKHR(device, &pipelineCreateInfo, &pipelineKey);
Create pipeline allowing for future binary creation
VkPipelineCreateFlags2CreateInfoKHR createFlags = {
VK_STRUCTURE_TYPE_PIPELINE_CREATE_FLAGS_2_CREATE_INFO_KHR
};
createFlags.flags = VK_PIPELINE_CREATE_2_CAPTURE_DATA_BIT_KHR;
graphicsCreateInfo.pNext = &createFlags;
// Create the pipeline
VkPipeline graphicsPipeline;
vkCreateGraphicsPipelines(device, NULL, 1, &graphicsCreateInfo, NULL, &graphicsPipeline);
Get new binaries and store to application cache
VkPipelineBinaryCreateInfoKHR createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_BINARY_CREATE_INFO_KHR;
createInfo.pNext = NULL;
createInfo.pKeysAndDataInfo = NULL;
createInfo.pipeline = graphicsPipeline;
createInfo.pPipelineCreateInfo = NULL;
VkPipelineBinaryHandlesInfoKHR handlesInfo;
handlesInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_BINARY_HANDLES_INFO_KHR;
handlesInfo.pNext = NULL;
handlesInfo.pipelineBinaryCount = 0;
handlesInfo.pPipelineBinaries = NULL;
vkCreatePipelineBinariesKHR(device, &createInfo, NULL, &handlesInfo);
std::vector<VkPipelineBinaryKHR> pipelineBinaries;
pipelineBinaries.resize(handlesInfo.pipelineBinaryCount);
handlesInfo.pPipelineBinaries = pipelineBinaries.data();
vkCreatePipelineBinariesKHR(device, &createInfo, NULL, &handlesInfo);
vector<VkPipelineBinaryKeyKHR> binaryKeys;
binaryKeys.resize(handlesInfo.pipelineBinaryCount);
// Store to application cache
for (int i = 0; i < handlesInfo.pipelineBinaryCount; ++i) {
VkPipelineBinaryDataInfoKHR binaryInfo;
binaryInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_BINARY_DATA_INFO_KHR;
binaryInfo.pNext = NULL;
binaryInfo.pipelineBinary = pipelineBinaries[i];
size_t binaryDataSize = 0;
vkGetPipelineBinaryDataKHR(device, &binaryInfo, &binaryKeys[i], &binaryDataSize, NULL);
vector<uint_8> data;
binaryData.resize(binaryDataSize);
vkGetPipelineBinaryDataKHR(device, &binaryInfo, &binaryKeys[i], &binaryDataSize, binaryData.data());
ApplicationBinaryCache.insert(binaryKeys[i], binaryData);
}
// Store pipeline key -> binary keys mapping
ApplicationCache.insert(pipelineKey, binaryKeys);
// Free any possible resources associated with binary creation for the pipeline
vkReleaseCapturedPipelineDataKHR(device, graphicsPipeline, NULL);
Get binaries from application cache
// Get the pipeline key
VkPipelineCreateInfoKHR pipelineCreateInfo;
pipelineCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CREATE_INFO_KHR;
pipelineCreateInfo.pNext = &graphicsCreateInfo;
VkPipelineBinaryKeyKHR pipelineKey;
vkGetPipelineKeyKHR(device, &pipelineCreateInfo, &pipelineKey);
// Get the binary keys
vector<VkPipelineBinaryKeyKHR> binaryKeys;
ApplicationCache.get(pipelineKey, binaryKeys);
// Get the binary data
std::vector<VkPipelineBinaryDataKHR> pipelineDatas;
pipelineDatas.resize(binaryKeys.size());
for (int i = 0; i < binaryKeys.size(); ++i) {
// Retrieve VkPipelineBinaryKHR handle from cache
ApplicationBinaryCache.get(binaryKeys[i], &pipelineDatas[i]);
}
Create binaries from application cache for a pipeline create info
VkPipelineBinaryKeysAndDataKHR binaryKeysAndData;
binaryKeysAndData.binaryCount = binaryKeys.size();
binaryKeysAndData.pPipelineBinaryKeys = binaryKeys.data();
binaryKeysAndData.pPipelineBinaryData = pipelineDatas.data();
VkPipelineBinaryCreateInfoKHR createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_BINARY_CREATE_INFO_KHR;
createInfo.pNext = NULL;
createInfo.pKeysAndDataInfo = &binaryKeysAndData;
createInfo.pipeline = VK_NULL_HANDLE;
createInfo.pPipelineCreateInfo = NULL;
std::vector<VkPipelineBinaryKHR> pipelineBinaries;
pipelineBinaries.resize(binaryKeysAndData.binaryCount);
VkPipelineBinaryHandlesInfoKHR handlesInfo;
handlesInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_BINARY_HANDLES_INFO_KHR;
handlesInfo.pNext = NULL;
handlesInfo.pipelineBinaryCount = binaryKeysAndData.binaryCount;
handlesInfo.pPipelineBinaries = pipelineBinaries.data();
vkCreatePipelineBinariesKHR(device, createInfo, NULL, &handlesInfo);
Create pipeline from binaries
VkPipelineBinaryInfoKHR binaryInfo = {
VK_STRUCTURE_TYPE_PIPELINE_BINARY_INFO_KHR,
NULL,
binaryCount,
pipelineBinaries.data()
};
createInfo.pNext = &binaryInfo;
// Create the pipeline
VkPipeline graphicsPipeline;
vkCreateGraphicsPipelines(device, NULL, 1, &createInfo, NULL, &graphicsPipeline);
Read internal cache for the pipeline binaries
VkGraphicsPipelineCreateInfo graphicsCreateInfo;
VkPipelineCreateInfoKHR pipelineCreateInfo;
pipelineCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CREATE_INFO_KHR;
pipelineCreateInfo.pNext = &graphicsCreateInfo;
VkPipelineBinaryCreateInfoKHR createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_BINARY_CREATE_INFO_KHR;
createInfo.pNext = NULL;
createInfo.pKeysAndDataInfo = NULL;
createInfo.pipeline = VK_NULL_HANDLE;
createInfo.pPipelineCreateInfo = &pipelineCreateInfo;
VkPipelineBinaryHandlesInfoKHR handlesInfo;
handlesInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_BINARY_HANDLES_INFO_KHR;
handlesInfo.pNext = NULL;
handlesInfo.pipelineBinaryCount = 0;
handlesInfo.pPipelineBinaries = NULL;
vkCreatePipelineBinariesKHR(device, &createInfo, NULL, &handlesInfo);
if (res == VK_PIPELINE_BINARY_MISSING_KHR) {
// Attempted to create a pipeline binary, but implementation does not have it in cache.
// Similar to VK_PIPELINE_COMPILE_REQUIRED, this is a positive return value.
return;
}
std::vector<VkPipelineBinaryKHR> pipelineBinaries;
pipelineBinaries.resize(handlesInfo.pipelineBinaryCount);
handlesInfo.pPipelineBinaries = pipelineBinaries.data();
vkCreatePipelineBinariesKHR(device, &createInfo, NULL, &handlesInfo);
Interactions with other extensions
VK_EXT_shader_module_identifier
already exposes some functionality of this extension,
the ability to omit SPIR-V during pipeline compilation.
This extension and module identifiers are intended to solve two different
use cases however.
Module identifiers have fuzzy guarantees and are intended for
implicit pipeline caching, i.e. caching that lives outside the knowledge of applications
in the context of translation layers and similar.
Pipeline binaries focus on enabling explicit caching mechanisms which applications have full control over. The pipeline binaries are directly exposed, so strong guarantees can be provided to applications on the success of compiling those pipelines. On platforms without implicit pipeline caching, pipeline binaries can serve as a stronger caching mechanism.
Another useful interaction is that vkGetPipelineKeyKHR
can generate a key for pipeline stages
which just take a VkPipelineShaderStageModuleIdentifierCreateInfoEXT
.
When using VK_EXT_graphics_pipeline_library
, keys can be generated, and binaries created, for individual pipeline libraries. These binaries can be used
in subsequent runs to recreate the pipeline libraries for linking into complete graphics pipelines.
Issues
RESOLVED: Fixed size keys
The original design had fixed size keys. We have decided that variable length keys with a limit will provide better flexibility without compromising the API usage too much.
It also matches the design outlined in VK_EXT_shader_module_identifier
.
RESOLVED: Should implementations be able to advertise in some way what a key/data pair are associated with?
This could allow applications to make more informed decisions about how to store key/data pairs - e.g. by grouping key/data pairs in separate maps depending on what they are.
This could take a number of forms - it might be as simple as an ID indicating like key/data pairs, or as complex as identifying particular parts of a pipeline and a cache level.
For applications wanting to precompile all possible pipelines, this would allow them to discard anything that is not a final binary, reducing the storage requirements.
Marking as resolved as it was decided that implementations would not generate non-final binaries.
RESOLVED: Can we avoid copies everywhere?
The current design necessitates copying binaries into the driver using vkCreatePipelineBinariesKHR. Could this be avoided by making the application allocate special memory up front and writing into it? Does that even save anything? Presumably CPU drivers will want to CPU inspect the binary anyway.
We also need to copy data out of the driver, and one copy is probably unavoidable because applications will need a CPU copy to write out to disk.
After much discussion, it was decided that there is not a great way to do this in a cross platform way that would be worth the marginal benefit.
RESOLVED: Can we avoid recomputing keys on each run?
The only key that needs to be recomputed between runs is the global key. Applications can assume that as long as the global key has not changed, they can reuse their previously computed keys.