From: Stefan Dösinger stefan@codeweavers.com
--- libs/vkd3d/device.c | 78 ++++++++++++++++++++++++++++++++++++-- libs/vkd3d/vkd3d_private.h | 32 ++++++++++++++++ 2 files changed, 107 insertions(+), 3 deletions(-)
diff --git a/libs/vkd3d/device.c b/libs/vkd3d/device.c index 8d0671add..b4ad0bdd1 100644 --- a/libs/vkd3d/device.c +++ b/libs/vkd3d/device.c @@ -19,8 +19,17 @@ #include "vkd3d_private.h" #include "vkd3d_version.h"
+#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + #define VKD3D_MAX_UAV_CLEAR_DESCRIPTORS_PER_TYPE 256u
+/* FIXME: We may want to put the GPU and driver identities in there, + * although under which conditions the pipeline cache can be transfered + * from one GPU/driver to another is a Vulkan implementation detail. */ +static const char vk_pipeline_cache_key[] = "vk_pipeline_cache"; + struct vkd3d_struct { enum vkd3d_structure_type type; @@ -2096,15 +2105,26 @@ static HRESULT d3d12_device_init_pipeline_cache(struct d3d12_device *device) { const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; VkPipelineCacheCreateInfo cache_info; + struct vkd3d_shader_cache_vk_blob *cache_data = NULL; + uint32_t cache_size = 0; VkResult vr;
vkd3d_mutex_init(&device->pipeline_cache_mutex);
+ if (!vkd3d_shader_cache_get(device->persistent_cache, vk_pipeline_cache_key, + sizeof(vk_pipeline_cache_key), NULL, &cache_size)) + { + cache_data = vkd3d_malloc(cache_size); + vkd3d_shader_cache_get(device->persistent_cache, vk_pipeline_cache_key, + sizeof(vk_pipeline_cache_key), cache_data, &cache_size); + cache_size -= offsetof(struct vkd3d_shader_cache_vk_blob, blob[0]); + } + cache_info.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; cache_info.pNext = NULL; cache_info.flags = 0; - cache_info.initialDataSize = 0; - cache_info.pInitialData = NULL; + cache_info.initialDataSize = cache_size; + cache_info.pInitialData = cache_data->blob; if ((vr = VK_CALL(vkCreatePipelineCache(device->vk_device, &cache_info, NULL, &device->vk_pipeline_cache))) < 0) { @@ -2112,15 +2132,40 @@ static HRESULT d3d12_device_init_pipeline_cache(struct d3d12_device *device) device->vk_pipeline_cache = VK_NULL_HANDLE; }
+ vkd3d_free(cache_data); + return S_OK; }
static void d3d12_device_destroy_pipeline_cache(struct d3d12_device *device) { const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; + struct vkd3d_shader_cache_vk_blob *cache_data = NULL; + size_t cache_size = 0; + VkResult vr;
if (device->vk_pipeline_cache) + { + vr = VK_CALL(vkGetPipelineCacheData(device->vk_device, device->vk_pipeline_cache, &cache_size, NULL)); + if (vr == VK_SUCCESS && cache_size) + cache_data = vkd3d_malloc(offsetof(struct vkd3d_shader_cache_vk_blob, blob[cache_size])); + if (cache_data) + { + cache_data->header.type = SHADER_CACHE_ENTRY_VULKAN_BLOB; + cache_data->header.vkd3d_revision = VKD3D_SHADER_CACHE_VKD3D_VERSION; + vr = VK_CALL(vkGetPipelineCacheData(device->vk_device, device->vk_pipeline_cache, + &cache_size, cache_data->blob)); + if (vr == VK_SUCCESS) + { + vkd3d_shader_cache_put(device->persistent_cache, vk_pipeline_cache_key, + sizeof(vk_pipeline_cache_key), cache_data, + offsetof(struct vkd3d_shader_cache_vk_blob, blob[cache_size])); + } + vkd3d_free(cache_data); + } + VK_CALL(vkDestroyPipelineCache(device->vk_device, device->vk_pipeline_cache, NULL)); + }
vkd3d_mutex_destroy(&device->pipeline_cache_mutex); } @@ -2587,6 +2632,7 @@ static ULONG STDMETHODCALLTYPE d3d12_device_Release(ID3D12Device5 *iface) if (device->use_vk_heaps) device_worker_stop(device); vkd3d_free(device->heaps); + vkd3d_shader_cache_close(device->persistent_cache); VK_CALL(vkDestroyDevice(device->vk_device, NULL)); if (device->parent) IUnknown_Release(device->parent); @@ -4355,7 +4401,9 @@ static void *device_worker_main(void *arg) static HRESULT d3d12_device_init(struct d3d12_device *device, struct vkd3d_instance *instance, const struct vkd3d_device_create_info *create_info) { + struct vkd3d_shader_cache_desc cache_desc = {0}; const struct vkd3d_vk_device_procs *vk_procs; + char *cache_name, *cwd; HRESULT hr;
device->ID3D12Device5_iface.lpVtbl = &d3d12_device_vtbl; @@ -4382,8 +4430,30 @@ static HRESULT d3d12_device_init(struct d3d12_device *device, if (FAILED(hr = vkd3d_create_vk_device(device, create_info))) goto out_free_instance;
+ /* FIXME: Does this use of getcwd work on Unix too? */ + cwd = getcwd(NULL, 0); + cache_name = vkd3d_malloc(strlen(cwd) + strlen(instance->application_name) + 8); + sprintf(cache_name, "%s/%s.cache", cwd, instance->application_name); + free(cwd); /* Use libc's free() because it is malloc'ed by getcwd. */ + + cache_desc.mem_size = 32 << 20; + cache_desc.disk_size = ~0u; + cache_desc.max_entries = ~0u; + cache_desc.version = VKD3D_SHADER_CACHE_OBJ_VERSION; + if (vkd3d_shader_cache_open(cache_name, &cache_desc, &device->persistent_cache)) + { + FIXME("Failed to open shader cache %s\n", debugstr_a(cache_name)); + cache_desc.disk_size = 0; + if (vkd3d_shader_cache_open(cache_name, &cache_desc, &device->persistent_cache)) + { + vkd3d_free(cache_name); + goto out_free_vk_resources; + } + } + vkd3d_free(cache_name); + if (FAILED(hr = d3d12_device_init_pipeline_cache(device))) - goto out_free_vk_resources; + goto out_free_cache;
if (FAILED(hr = vkd3d_private_store_init(&device->private_store))) goto out_free_pipeline_cache; @@ -4436,6 +4506,8 @@ out_free_private_store: vkd3d_private_store_destroy(&device->private_store); out_free_pipeline_cache: d3d12_device_destroy_pipeline_cache(device); +out_free_cache: + vkd3d_shader_cache_close(device->persistent_cache); out_free_vk_resources: vk_procs = &device->vk_procs; VK_CALL(vkDestroyDevice(device->vk_device, NULL)); diff --git a/libs/vkd3d/vkd3d_private.h b/libs/vkd3d/vkd3d_private.h index ad1c03397..acd010521 100644 --- a/libs/vkd3d/vkd3d_private.h +++ b/libs/vkd3d/vkd3d_private.h @@ -43,6 +43,37 @@ #include <limits.h> #include <stdbool.h>
+/* The following structures define data structures that are stored in vkd3d's + * cache. It doesn't define the cache format itself, those details are found + * in cache.c. + * + * Changing the structures will break compatibility with existing cache files. + * In this case bump VKD3D_SHADER_CACHE_OBJ_VERSION. + * + * The structs aren't meant to be read by external code, consider them a vkd3d + * implementation detail. */ +#define VKD3D_SHADER_CACHE_OBJ_VERSION 1ull +#define VKD3D_SHADER_CACHE_VKD3D_VERSION 1u + +enum vkd3d_shader_cache_entry_type +{ + SHADER_CACHE_ENTRY_VULKAN_BLOB = VKD3D_MAKE_TAG('V', 'K', 'P', 'C'), +}; + +struct vkd3d_shader_cache_entry +{ + uint32_t vkd3d_revision; /* Put the git revision here, discard translated code if changed. */ + uint32_t type; +}; + +struct vkd3d_shader_cache_vk_blob +{ + struct vkd3d_shader_cache_entry header; + uint8_t blob[1]; +}; + +/* End shader data structures */ + #define VK_CALL(f) (vk_procs->f)
#define VKD3D_DESCRIPTOR_MAGIC_FREE 0x00000000u @@ -1780,6 +1811,7 @@ struct d3d12_device bool worker_should_exit;
struct vkd3d_mutex pipeline_cache_mutex; + struct vkd3d_shader_cache *persistent_cache; struct vkd3d_shader_cache *render_pass_cache; VkPipelineCache vk_pipeline_cache;