From: Stefan Dösinger stefan@codeweavers.com
--- libs/vkd3d/cache.c | 243 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 243 insertions(+)
diff --git a/libs/vkd3d/cache.c b/libs/vkd3d/cache.c index 6ca261507..049cf00ed 100644 --- a/libs/vkd3d/cache.c +++ b/libs/vkd3d/cache.c @@ -32,6 +32,21 @@ static LONG cache_mutex_initialized; * * We don't intend these files to be read by third party code, so consider them a vkd3d * implementation detail. */ + +/* TODO: Endinaness of all these uints. */ + +/* VKD3DSHC */ +#define VKD3D_SHADER_CACHE_MAGIC 0x564B443344534843ull +#define VKD3D_SHADER_CACHE_VERSION ((uint64_t)1) + +struct vkd3d_cache_header_v1 +{ + uint64_t magic; + uint64_t struct_size; + uint64_t vkd3d_version; + uint64_t app_version; +}; + struct vkd3d_cache_object_v1 { uint64_t hash; @@ -52,6 +67,8 @@ struct vkd3d_shader_cache struct vkd3d_mutex lock; struct rb_tree tree;
+ FILE *indices, *values; + char name[1]; };
@@ -84,6 +101,145 @@ static void vkd3d_shader_cache_remove_item(struct vkd3d_shader_cache *cache, str rb_remove(&cache->tree, &e->entry); }
+static bool vkd3d_shader_cache_read_entry(struct vkd3d_shader_cache *cache, struct shader_cache_entry *e) +{ + size_t len; + + TRACE("reading object key len %u, data %ud.\n", e->d.key_size, e->d.value_size); + /* TODO: Check if the read size makes sense - is it smaller than the requested + * max size, is it smaller than the file on the disk etc. */ + e->payload = vkd3d_malloc(e->d.key_size + e->d.value_size); + if (!e->payload) + { + WARN("Out of memory.\n"); + return false; + } + + if (e->d.disk_size != e->d.key_size + e->d.value_size) + ERR("How do I get a compressed object before implementing compression?\n"); + + fseek(cache->values, e->d.offset, SEEK_SET); + len = fread(e->payload, e->d.key_size + e->d.value_size, 1, cache->values); + if (len != 1) + { + /* I suppose this could be handled better. */ + ERR("Failed to read cached object data len %u offset %u.\n", + e->d.key_size + e->d.value_size, e->d.offset); + vkd3d_free(e->payload); + return false; + } + + return true; +} + +static void vkd3d_shader_cache_read(struct vkd3d_shader_cache *cache) +{ + const bool ro = cache->desc.flags & VKD3D_SHADER_CACHE_FLAGS_READ_ONLY; + struct shader_cache_entry *e = NULL; + struct vkd3d_cache_header_v1 hdr; + char *filename; + FILE *indices; + size_t len; + + filename = vkd3d_malloc(strlen(cache->name) + 5); + + sprintf(filename, "%s.val", cache->name); + cache->values = fopen(filename, ro ? "rb" : "r+b"); + if (!cache->values) + { + if (ro) + { + WARN("Read only cache file %s not found.\n", filename); + return; + } + + cache->values = fopen(filename, "w+b"); + if (!cache->values) + { + WARN("Value file %s not found and could not be created.\n", filename); + /* Convert to mem only. */ + cache->desc.disk_size = 0; + cache->desc.flags |= VKD3D_SHADER_CACHE_FLAGS_MEMORY_ONLY; + vkd3d_free(filename); + return; + } + } + + sprintf(filename, "%s.idx", cache->name); + indices = fopen(filename, "rb"); + if (!indices) + { + /* This happens when the cache files did not exist. Keep the opened + * values file, we'll use it later. */ + WARN("Index file %s not found.\n", filename); + vkd3d_free(filename); + return; + } + + vkd3d_free(filename); + + TRACE("Reading cache %s.{idx, val}.\n", cache->name); + + len = fread(&hdr, sizeof(hdr), 1, indices); + if (len != 1) + { + WARN("Failed to read cache header.\n"); + goto done; + } + if (hdr.magic != VKD3D_SHADER_CACHE_MAGIC) + { + WARN("Invalid cache magic.\n"); + goto done; + } + if (hdr.struct_size < sizeof(hdr)) + { + WARN("Invalid cache header size.\n"); + goto done; + } + if (hdr.vkd3d_version != VKD3D_SHADER_CACHE_VERSION) + { + WARN("vkd3d shader version mismatch: Got %"PRIu64", want %"PRIu64".\n", + hdr.vkd3d_version, VKD3D_SHADER_CACHE_VERSION); + goto done; + } + if (hdr.app_version != cache->desc.version) + { + WARN("Application version mismatch: Cache has %"PRIu64", app wants %"PRIu64".\n", + hdr.app_version, cache->desc.version); + goto done; + } + + while (!feof(indices)) + { + e = vkd3d_calloc(1, sizeof(*e)); + if (!e) + { + WARN("Alloc fail.\n"); + break; + } + + len = fread(&e->d, sizeof(e->d), 1, indices); + if (len != 1) + { + if (!feof(indices)) + ERR("Failed to read object header.\n"); + break; + } + + if (!vkd3d_shader_cache_read_entry(cache, e)) + break; + + vkd3d_shader_cache_add_item(cache, e); + + TRACE("Loaded an item.\n"); + e = NULL; + } + +done: + vkd3d_free(e); + fclose(indices); +} + int vkd3d_shader_cache_open(const char *name, const struct vkd3d_shader_cache_desc *desc, struct vkd3d_shader_cache **cache) { @@ -98,6 +254,17 @@ int vkd3d_shader_cache_open(const char *name, return VKD3D_ERROR_INVALID_ARGUMENT; }
+ if (desc->disk_size && (desc->flags & VKD3D_SHADER_CACHE_FLAGS_MEMORY_ONLY)) + { + WARN("Disk size %lu with memory only flag set.\n", desc->disk_size); + return VKD3D_ERROR_INVALID_ARGUMENT; + } + if (!desc->disk_size && !(desc->flags & VKD3D_SHADER_CACHE_FLAGS_MEMORY_ONLY)) + { + WARN("On-disk cache of size 0.\n", desc->disk_size); + return VKD3D_ERROR_INVALID_ARGUMENT; + } + /* FIXME: This isn't thread safe and cache_mutex_initialized might overflow. Do we have a * something like DllMain or a platform-independent InitializeOnce? */ if (InterlockedIncrement(&cache_mutex_initialized) == 1) @@ -139,6 +306,9 @@ int vkd3d_shader_cache_open(const char *name, list_add_head(&cache_list, &object->cache_list_entry); vkd3d_mutex_unlock(&cache_list_mutex);
+ if (!(desc->flags & VKD3D_SHADER_CACHE_FLAGS_MEMORY_ONLY)) + vkd3d_shader_cache_read(object); + *cache = object; return VKD3D_OK; } @@ -149,6 +319,76 @@ static void vkd3d_shader_cache_clear(struct rb_entry *entry, void *context) vkd3d_free(e); }
+struct write_context +{ + struct vkd3d_shader_cache *cache; + FILE *indices; +}; + +static void vkd3d_shader_cache_write_entry(struct rb_entry *entry, void *context) +{ + struct shader_cache_entry *e = RB_ENTRY_VALUE(entry, struct shader_cache_entry, entry); + struct write_context *ctx = context; + struct vkd3d_shader_cache *cache = ctx->cache; + + /* TODO: Compress the data. */ + e->d.disk_size = e->d.key_size + e->d.value_size; + e->d.offset = ftell(cache->values); + + fwrite(&e->d, sizeof(e->d), 1, ctx->indices); + fwrite(e->payload, e->d.disk_size, 1, cache->values); +} + +static void vkd3d_shader_cache_write(struct vkd3d_shader_cache *cache) +{ + struct vkd3d_cache_header_v1 hdr; + struct write_context ctx; + char *filename; + + if (cache->desc.flags & VKD3D_SHADER_CACHE_FLAGS_READ_ONLY) + { + fclose (cache->values); + return; + } + + fseek(cache->values, 0, SEEK_END); + + filename = vkd3d_malloc(strlen(cache->name) + 5); + /* For now unconditionally repack. */ + if (1) + { + fclose(cache->values); + sprintf(filename, "%s.val", cache->name); + cache->values = fopen(filename, "w+b"); + if (!cache->values) + ERR("Reopen fail\n"); + } + + sprintf(filename, "%s.idx", cache->name); + ctx.indices = fopen(filename, "wb"); + if (!ctx.indices) + { + WARN("Failed to open %s\n", filename); + vkd3d_free(filename); + return; + } + vkd3d_free(filename); + + ctx.cache = cache; + hdr.magic = VKD3D_SHADER_CACHE_MAGIC; + hdr.struct_size = sizeof(hdr); + hdr.vkd3d_version = VKD3D_SHADER_CACHE_VERSION; + hdr.app_version = cache->desc.version; + + fwrite(&hdr, sizeof(hdr), 1, ctx.indices); + + rb_for_each_entry(&cache->tree, vkd3d_shader_cache_write_entry, &ctx); + + fseek(cache->values, 0, SEEK_END); + fclose(cache->values); + fclose(ctx.indices); +} + void vkd3d_shader_cache_close(struct vkd3d_shader_cache *cache) { ULONG refcount = InterlockedDecrement(&cache->refcount); @@ -157,6 +397,9 @@ void vkd3d_shader_cache_close(struct vkd3d_shader_cache *cache) if (refcount) return;
+ if (!(cache->desc.flags & VKD3D_SHADER_CACHE_FLAGS_MEMORY_ONLY)) + vkd3d_shader_cache_write(cache); + vkd3d_mutex_lock(&cache_list_mutex); list_remove(&cache->cache_list_entry); vkd3d_mutex_unlock(&cache_list_mutex);