cache: Add concept of internal pipeline libraries.

For internal pipeline libraries, we want a somewhat different strategy.

- PSOs are keyed by hash instead of user key.
- We want the option to conditionally store SPIR-V and PSO blobs.
  For internal caches, there isn't much of a reason to store PSO blobs
  since the disk cache is going to be primed anyways.

Signed-off-by: Hans-Kristian Arntzen <post@arntzen-software.no>
This commit is contained in:
Hans-Kristian Arntzen 2022-02-02 14:10:48 +01:00
parent fb14dbece0
commit fcd9e2d0ac
3 changed files with 118 additions and 36 deletions

View File

@ -276,6 +276,23 @@ static const struct vkd3d_pipeline_blob_chunk *find_blob_chunk(const struct vkd3
return NULL;
}
static uint32_t d3d12_cached_pipeline_state_to_flags(const struct d3d12_cached_pipeline_state *state)
{
uint32_t pipeline_library_flags;
if (state->library)
pipeline_library_flags = state->library->flags;
else if (vkd3d_config_flags & VKD3D_CONFIG_FLAG_GLOBAL_PIPELINE_CACHE)
pipeline_library_flags = 0;
else
{
pipeline_library_flags = VKD3D_PIPELINE_LIBRARY_FLAG_USE_PIPELINE_CACHE_UUID |
VKD3D_PIPELINE_LIBRARY_FLAG_SAVE_PSO_BLOB;
}
return pipeline_library_flags;
}
HRESULT d3d12_cached_pipeline_state_validate(struct d3d12_device *device,
const struct d3d12_cached_pipeline_state *state,
const struct vkd3d_pipeline_cache_compatibility *compat)
@ -284,9 +301,12 @@ HRESULT d3d12_cached_pipeline_state_validate(struct d3d12_device *device,
const struct vkd3d_pipeline_blob *blob = state->blob.pCachedBlob;
const struct vkd3d_pipeline_blob_chunk_pso_compat *pso_compat;
const struct vkd3d_pipeline_blob_chunk *chunk;
uint32_t pipeline_library_flags;
size_t payload_size;
uint32_t checksum;
pipeline_library_flags = d3d12_cached_pipeline_state_to_flags(state);
/* Avoid E_INVALIDARG with an invalid header size, since that may confuse some games */
if (state->blob.CachedBlobSizeInBytes < sizeof(*blob) || blob->version != VKD3D_CACHE_BLOB_VERSION)
return D3D12_ERROR_DRIVER_VERSION_MISMATCH;
@ -302,10 +322,14 @@ HRESULT d3d12_cached_pipeline_state_validate(struct d3d12_device *device,
* Based on global configuration flags, which extensions are available, etc,
* the generated shaders may also change, so key on that as well. */
if (blob->vkd3d_build != vkd3d_build ||
blob->vkd3d_shader_interface_key != device->shader_interface_key ||
memcmp(blob->cache_uuid, device_properties->pipelineCacheUUID, VK_UUID_SIZE) != 0)
blob->vkd3d_shader_interface_key != device->shader_interface_key)
return D3D12_ERROR_DRIVER_VERSION_MISMATCH;
/* Only verify pipeline cache UUID if we're going to read anything from it. */
if (pipeline_library_flags & VKD3D_PIPELINE_LIBRARY_FLAG_USE_PIPELINE_CACHE_UUID)
if (memcmp(blob->cache_uuid, device_properties->pipelineCacheUUID, VK_UUID_SIZE) != 0)
return D3D12_ERROR_DRIVER_VERSION_MISMATCH;
checksum = vkd3d_pipeline_blob_compute_data_checksum(blob->data, payload_size);
if (checksum != blob->checksum)
@ -420,11 +444,20 @@ HRESULT vkd3d_create_pipeline_cache_from_d3d12_desc(struct d3d12_device *device,
const struct vkd3d_pipeline_blob *blob = state->blob.pCachedBlob;
const struct vkd3d_pipeline_blob_chunk_link *link;
const struct vkd3d_pipeline_blob_chunk *chunk;
uint32_t pipeline_library_flags;
size_t payload_size;
const void *data;
size_t size;
VkResult vr;
pipeline_library_flags = d3d12_cached_pipeline_state_to_flags(state);
if (!(pipeline_library_flags & VKD3D_PIPELINE_LIBRARY_FLAG_SAVE_PSO_BLOB))
{
vr = vkd3d_create_pipeline_cache(device, 0, NULL, cache);
return hresult_from_vk_result(vr);
}
if (!state->blob.CachedBlobSizeInBytes)
{
if (vkd3d_config_flags & VKD3D_CONFIG_FLAG_PIPELINE_LIBRARY_LOG)
@ -799,7 +832,7 @@ static VkResult vkd3d_serialize_pipeline_state_referenced(struct d3d12_pipeline_
entry.data.is_new = 1;
entry.data.state = NULL;
if (state->vk_pso_cache)
if (state->vk_pso_cache && (pipeline_library->flags & VKD3D_PIPELINE_LIBRARY_FLAG_SAVE_PSO_BLOB))
{
entry.data.blob_length = sizeof(*internal) + vk_pipeline_cache_size;
internal = vkd3d_malloc(entry.data.blob_length);
@ -843,21 +876,24 @@ static VkResult vkd3d_serialize_pipeline_state_referenced(struct d3d12_pipeline_
chunk = finish_and_iterate_blob_chunk(chunk);
}
if (d3d12_pipeline_state_is_graphics(state))
if (pipeline_library->flags & VKD3D_PIPELINE_LIBRARY_FLAG_SAVE_FULL_SPIRV)
{
for (i = 0; i < state->graphics.stage_count; i++)
if (d3d12_pipeline_state_is_graphics(state))
{
for (i = 0; i < state->graphics.stage_count; i++)
{
vkd3d_shader_code_serialize_referenced(pipeline_library,
&state->graphics.code[i], state->graphics.stages[i].stage,
varint_size[i], &chunk);
}
}
else if (d3d12_pipeline_state_is_compute(state))
{
vkd3d_shader_code_serialize_referenced(pipeline_library,
&state->graphics.code[i], state->graphics.stages[i].stage,
varint_size[i], &chunk);
&state->compute.code, VK_SHADER_STAGE_COMPUTE_BIT,
varint_size[0], &chunk);
}
}
else if (d3d12_pipeline_state_is_compute(state))
{
vkd3d_shader_code_serialize_referenced(pipeline_library,
&state->compute.code, VK_SHADER_STAGE_COMPUTE_BIT,
varint_size[0], &chunk);
}
return VK_SUCCESS;
}
@ -883,7 +919,7 @@ VkResult vkd3d_serialize_pipeline_state(struct d3d12_pipeline_library *pipeline_
/* PSO compatibility information is global to a PSO. */
vk_blob_size += VKD3D_PIPELINE_BLOB_CHUNK_SIZE(pso_compat);
if (state->vk_pso_cache)
if (state->vk_pso_cache && (!pipeline_library || (pipeline_library->flags & VKD3D_PIPELINE_LIBRARY_FLAG_SAVE_PSO_BLOB)))
{
if (need_blob_sizes)
{
@ -900,18 +936,21 @@ VkResult vkd3d_serialize_pipeline_state(struct d3d12_pipeline_library *pipeline_
vk_blob_size += VKD3D_PIPELINE_BLOB_CHUNK_SIZE_RAW(vk_blob_size_pipeline_cache);
}
if (d3d12_pipeline_state_is_graphics(state))
if (!pipeline_library || (pipeline_library->flags & VKD3D_PIPELINE_LIBRARY_FLAG_SAVE_FULL_SPIRV))
{
for (i = 0; i < state->graphics.stage_count; i++)
if (d3d12_pipeline_state_is_graphics(state))
{
vk_blob_size += vkd3d_shader_code_compute_serialized_size(&state->graphics.code[i],
need_blob_sizes ? &varint_size[i] : NULL, !pipeline_library);
for (i = 0; i < state->graphics.stage_count; i++)
{
vk_blob_size += vkd3d_shader_code_compute_serialized_size(&state->graphics.code[i],
need_blob_sizes ? &varint_size[i] : NULL, !pipeline_library);
}
}
else if (d3d12_pipeline_state_is_compute(state))
{
vk_blob_size += vkd3d_shader_code_compute_serialized_size(&state->compute.code,
need_blob_sizes ? &varint_size[0] : NULL, !pipeline_library);
}
}
else if (d3d12_pipeline_state_is_compute(state))
{
vk_blob_size += vkd3d_shader_code_compute_serialized_size(&state->compute.code,
need_blob_sizes ? &varint_size[0] : NULL, !pipeline_library);
}
total_size += vk_blob_size;
@ -926,7 +965,12 @@ VkResult vkd3d_serialize_pipeline_state(struct d3d12_pipeline_library *pipeline_
blob->device_id = device_properties->deviceID;
blob->vkd3d_shader_interface_key = state->device->shader_interface_key;
blob->vkd3d_build = vkd3d_build;
memcpy(blob->cache_uuid, device_properties->pipelineCacheUUID, VK_UUID_SIZE);
if (!pipeline_library || (pipeline_library->flags & VKD3D_PIPELINE_LIBRARY_FLAG_USE_PIPELINE_CACHE_UUID))
memcpy(blob->cache_uuid, device_properties->pipelineCacheUUID, VK_UUID_SIZE);
else
memset(blob->cache_uuid, 0, VK_UUID_SIZE);
chunk = CAST_CHUNK_BASE(blob);
chunk->type = VKD3D_PIPELINE_BLOB_CHUNK_TYPE_PSO_COMPAT;
@ -1596,7 +1640,11 @@ static HRESULT d3d12_pipeline_library_serialize(struct d3d12_pipeline_library *p
header->driver_cache_count = pipeline_library->driver_cache_map.used_count;
header->vkd3d_build = vkd3d_build;
header->vkd3d_shader_interface_key = pipeline_library->device->shader_interface_key;
memcpy(header->cache_uuid, device_properties->pipelineCacheUUID, VK_UUID_SIZE);
if (pipeline_library->flags & VKD3D_PIPELINE_LIBRARY_FLAG_USE_PIPELINE_CACHE_UUID)
memcpy(header->cache_uuid, device_properties->pipelineCacheUUID, VK_UUID_SIZE);
else
memset(header->cache_uuid, 0, VK_UUID_SIZE);
total_toc_entries = header->pipeline_count + header->spirv_count + header->driver_cache_count;
@ -1816,11 +1864,15 @@ static HRESULT d3d12_pipeline_library_read_blob(struct d3d12_pipeline_library *p
return D3D12_ERROR_DRIVER_VERSION_MISMATCH;
}
if (memcmp(header->cache_uuid, device_properties->pipelineCacheUUID, VK_UUID_SIZE) != 0)
/* If we never store pipeline caches, we don't have to care about pipeline cache UUID. */
if (pipeline_library->flags & VKD3D_PIPELINE_LIBRARY_FLAG_USE_PIPELINE_CACHE_UUID)
{
if (vkd3d_config_flags & VKD3D_CONFIG_FLAG_PIPELINE_LIBRARY_LOG)
INFO("Rejecting pipeline library due to pipelineCacheUUID mismatch.\n");
return D3D12_ERROR_DRIVER_VERSION_MISMATCH;
if (memcmp(header->cache_uuid, device_properties->pipelineCacheUUID, VK_UUID_SIZE) != 0)
{
if (vkd3d_config_flags & VKD3D_CONFIG_FLAG_PIPELINE_LIBRARY_LOG)
INFO("Rejecting pipeline library due to pipelineCacheUUID mismatch.\n");
return D3D12_ERROR_DRIVER_VERSION_MISMATCH;
}
}
total_toc_entries = header->pipeline_count + header->spirv_count + header->driver_cache_count;
@ -1878,8 +1930,9 @@ static HRESULT d3d12_pipeline_library_read_blob(struct d3d12_pipeline_library *p
}
static HRESULT d3d12_pipeline_library_init(struct d3d12_pipeline_library *pipeline_library,
struct d3d12_device *device, const void *blob, size_t blob_length)
struct d3d12_device *device, const void *blob, size_t blob_length, uint32_t flags)
{
bool internal_keys;
HRESULT hr;
int rc;
@ -1887,6 +1940,7 @@ static HRESULT d3d12_pipeline_library_init(struct d3d12_pipeline_library *pipeli
pipeline_library->ID3D12PipelineLibrary_iface.lpVtbl = &d3d12_pipeline_library_vtbl;
pipeline_library->refcount = 1;
pipeline_library->internal_refcount = 1;
pipeline_library->flags = flags;
if (!blob_length && blob)
return E_INVALIDARG;
@ -1900,12 +1954,16 @@ static HRESULT d3d12_pipeline_library_init(struct d3d12_pipeline_library *pipeli
return hresult_from_errno(rc);
}
internal_keys = !!(flags & VKD3D_PIPELINE_LIBRARY_FLAG_INTERNAL_KEYS);
hash_map_init(&pipeline_library->spirv_cache_map, vkd3d_cached_pipeline_hash_internal,
vkd3d_cached_pipeline_compare_internal, sizeof(struct vkd3d_cached_pipeline_entry));
hash_map_init(&pipeline_library->driver_cache_map, vkd3d_cached_pipeline_hash_internal,
vkd3d_cached_pipeline_compare_internal, sizeof(struct vkd3d_cached_pipeline_entry));
hash_map_init(&pipeline_library->pso_map, vkd3d_cached_pipeline_hash_name,
vkd3d_cached_pipeline_compare_name, sizeof(struct vkd3d_cached_pipeline_entry));
hash_map_init(&pipeline_library->pso_map,
internal_keys ? vkd3d_cached_pipeline_hash_internal : vkd3d_cached_pipeline_hash_name,
internal_keys ? vkd3d_cached_pipeline_compare_internal : vkd3d_cached_pipeline_compare_name,
sizeof(struct vkd3d_cached_pipeline_entry));
if (blob_length)
{
@ -1943,7 +2001,7 @@ cleanup_mutex:
}
HRESULT d3d12_pipeline_library_create(struct d3d12_device *device, const void *blob,
size_t blob_length, struct d3d12_pipeline_library **pipeline_library)
size_t blob_length, uint32_t flags, struct d3d12_pipeline_library **pipeline_library)
{
struct d3d12_pipeline_library *object;
HRESULT hr;
@ -1951,7 +2009,7 @@ HRESULT d3d12_pipeline_library_create(struct d3d12_device *device, const void *b
if (!(object = vkd3d_malloc(sizeof(*object))))
return E_OUTOFMEMORY;
if (FAILED(hr = d3d12_pipeline_library_init(object, device, blob, blob_length)))
if (FAILED(hr = d3d12_pipeline_library_init(object, device, blob, blob_length, flags)))
{
vkd3d_free(object);
return hr;

View File

@ -4474,12 +4474,26 @@ static HRESULT STDMETHODCALLTYPE d3d12_device_CreatePipelineLibrary(d3d12_device
{
struct d3d12_device *device = impl_from_ID3D12Device(iface);
struct d3d12_pipeline_library *pipeline_library;
uint32_t flags;
HRESULT hr;
TRACE("iface %p, blob %p, blob_size %lu, iid %s, lib %p.\n",
iface, blob, blob_size, debugstr_guid(iid), lib);
if (FAILED(hr = d3d12_pipeline_library_create(device, blob, blob_size, &pipeline_library)))
flags = 0;
if (!(vkd3d_config_flags & VKD3D_CONFIG_FLAG_PIPELINE_LIBRARY_NO_SERIALIZE_SPIRV))
flags |= VKD3D_PIPELINE_LIBRARY_FLAG_SAVE_FULL_SPIRV;
/* If we're using global pipeline caches, these are irrelevant. */
if (!(vkd3d_config_flags & VKD3D_CONFIG_FLAG_GLOBAL_PIPELINE_CACHE))
{
flags |= VKD3D_PIPELINE_LIBRARY_FLAG_SAVE_PSO_BLOB |
VKD3D_PIPELINE_LIBRARY_FLAG_USE_PIPELINE_CACHE_UUID;
}
if (FAILED(hr = d3d12_pipeline_library_create(device, blob, blob_size,
flags, &pipeline_library)))
return hr;
if (lib)

View File

@ -1669,6 +1669,7 @@ struct d3d12_pipeline_library
d3d12_pipeline_library_iface ID3D12PipelineLibrary_iface;
LONG refcount;
LONG internal_refcount;
uint32_t flags;
struct d3d12_device *device;
@ -1687,8 +1688,17 @@ struct d3d12_pipeline_library
struct vkd3d_private_store private_store;
};
enum vkd3d_pipeline_library_flags
{
VKD3D_PIPELINE_LIBRARY_FLAG_SAVE_FULL_SPIRV = 1 << 0,
VKD3D_PIPELINE_LIBRARY_FLAG_SAVE_PSO_BLOB = 1 << 1,
VKD3D_PIPELINE_LIBRARY_FLAG_INTERNAL_KEYS = 1 << 2,
VKD3D_PIPELINE_LIBRARY_FLAG_USE_PIPELINE_CACHE_UUID = 1 << 3,
};
HRESULT d3d12_pipeline_library_create(struct d3d12_device *device, const void *blob,
size_t blob_length, struct d3d12_pipeline_library **pipeline_library);
size_t blob_length, uint32_t flags, /* vkd3d_pipeline_library_flags */
struct d3d12_pipeline_library **pipeline_library);
VkResult vkd3d_create_pipeline_cache(struct d3d12_device *device,
size_t size, const void *data, VkPipelineCache *cache);