From: Stefan Dösinger stefan@codeweavers.com
---
No flags parameter for put and get for now. The internal use I had (a flag to prevent replacing existing values) is actually the default of native ID3D12ShaderCacheSession and for the time being fine for internal uses. We should probably add a flags parameter before making the API public. --- include/vkd3d_types.h | 2 + libs/vkd3d-common/error.c | 2 + libs/vkd3d/cache.c | 144 +++++++++++++++++++++++++++++++++++++ libs/vkd3d/device.c | 16 ++++- libs/vkd3d/vkd3d_private.h | 2 + 5 files changed, 163 insertions(+), 3 deletions(-)
diff --git a/include/vkd3d_types.h b/include/vkd3d_types.h index 9060fa087..4334f28f0 100644 --- a/include/vkd3d_types.h +++ b/include/vkd3d_types.h @@ -53,6 +53,8 @@ enum vkd3d_result VKD3D_ERROR_INVALID_SHADER = -4, /** The operation is not implemented in this version of vkd3d. */ VKD3D_ERROR_NOT_IMPLEMENTED = -5, + /** The key already exists in the cache. */ + VKD3D_ERROR_KEY_ALREADY_EXISTS = -6,
VKD3D_FORCE_32_BIT_ENUM(VKD3D_RESULT), }; diff --git a/libs/vkd3d-common/error.c b/libs/vkd3d-common/error.c index b8350a540..a6efeddec 100644 --- a/libs/vkd3d-common/error.c +++ b/libs/vkd3d-common/error.c @@ -35,6 +35,8 @@ HRESULT hresult_from_vkd3d_result(int vkd3d_result) return E_INVALIDARG; case VKD3D_ERROR_NOT_IMPLEMENTED: return E_NOTIMPL; + case VKD3D_ERROR_KEY_ALREADY_EXISTS: + return DXGI_ERROR_ALREADY_EXISTS; default: FIXME("Unhandled vkd3d result %d.\n", vkd3d_result); return E_FAIL; diff --git a/libs/vkd3d/cache.c b/libs/vkd3d/cache.c index 56ba69904..1ff567e9b 100644 --- a/libs/vkd3d/cache.c +++ b/libs/vkd3d/cache.c @@ -18,11 +18,64 @@
#include "vkd3d_private.h"
+struct vkd3d_cache_entry_header +{ + uint64_t hash; + uint64_t key_size; + uint64_t value_size; +}; + struct vkd3d_shader_cache { unsigned int refcount; + struct rb_tree tree; +}; + +struct shader_cache_entry +{ + struct vkd3d_cache_entry_header h; + struct rb_entry entry; + uint8_t *payload; };
+struct shader_cache_key +{ + uint64_t hash; + const void *key; + uint64_t key_size; +}; + +static int vkd3d_shader_cache_compare_key(const void *key, const struct rb_entry *entry) +{ + const struct shader_cache_entry *e = RB_ENTRY_VALUE(entry, struct shader_cache_entry, entry); + const struct shader_cache_key *k = key; + int ret; + + if (k->hash < e->h.hash) + return -1; + if (k->hash > e->h.hash) + return 1; + + if (k->key_size < e->h.key_size) + return -1; + if (k->key_size > e->h.key_size) + return 1; + + /* Until now we have not seen an actual hash collission. If the key didn't match it was always + * due to a bug in the serialization code or memory corruption. If you see this FIXME please + * investigate. */ + ret = memcmp(k->key, e->payload, k->key_size); + if (ret) + FIXME("Actual case of a hash collission found.\n"); + return ret; +} + +static void vkd3d_shader_cache_add_item(struct vkd3d_shader_cache *cache, + struct shader_cache_entry *e) +{ + rb_put(&cache->tree, &e->h.hash, &e->entry); +} + int vkd3d_shader_open_cache(struct vkd3d_shader_cache **cache) { struct vkd3d_shader_cache *object; @@ -34,6 +87,8 @@ int vkd3d_shader_open_cache(struct vkd3d_shader_cache **cache) return VKD3D_ERROR_OUT_OF_MEMORY;
object->refcount = 1; + rb_init(&object->tree, vkd3d_shader_cache_compare_key); + *cache = object;
return VKD3D_OK; @@ -46,6 +101,13 @@ unsigned int vkd3d_shader_cache_incref(struct vkd3d_shader_cache *cache) return refcount; }
+static void vkd3d_shader_cache_clear(struct rb_entry *entry, void *context) +{ + struct shader_cache_entry *e = RB_ENTRY_VALUE(entry, struct shader_cache_entry, entry); + vkd3d_free(e->payload); + vkd3d_free(e); +} + unsigned int vkd3d_shader_cache_decref(struct vkd3d_shader_cache *cache) { unsigned int refcount = vkd3d_atomic_decrement_u32(&cache->refcount); @@ -54,6 +116,88 @@ unsigned int vkd3d_shader_cache_decref(struct vkd3d_shader_cache *cache) if (refcount) return refcount;
+ rb_destroy(&cache->tree, vkd3d_shader_cache_clear, NULL); + vkd3d_free(cache); return 0; } + +static const uint64_t fnv_offset = 0xcbf29ce484222325; +static const uint64_t fnv_prime = 0x00000100000001b3; + +static inline uint64_t fnv_1a_64(const uint64_t *values, size_t count, uint64_t seed) +{ + size_t i; + + for (i = 0; i < count; ++i) + seed = (seed ^ values[i]) * fnv_prime; + + return seed; +} + +static uint64_t vkd3d_shader_cache_hash_key(const void *key, size_t size) +{ + uint64_t last = 0, ret; + + ret = fnv_1a_64(key, size / sizeof(uint64_t), fnv_offset); + if (size % sizeof(uint64_t)) + { + const char *c = key; + /* FIXME: Endianess? */ + c += align(size, sizeof(uint64_t)) - sizeof(uint64_t); + memcpy(&last, c, size % sizeof(uint64_t)); + ret = fnv_1a_64(&last, 1, ret); + } + return ret; +} + +int vkd3d_shader_cache_put(struct vkd3d_shader_cache *cache, + const void *key, size_t key_size, const void *value, size_t value_size) +{ + struct shader_cache_entry *e; + struct shader_cache_key k; + struct rb_entry *entry; + enum vkd3d_result ret; + + TRACE("%p, %p, %#zx, %p, %#zx.\n", cache, key, key_size, value, value_size); + + k.hash = vkd3d_shader_cache_hash_key(key, key_size); + k.key = key; + k.key_size = key_size; + entry = rb_get(&cache->tree, &k); + e = entry ? RB_ENTRY_VALUE(entry, struct shader_cache_entry, entry) : NULL; + + if (e) + { + WARN("Key already exists, returning VKD3D_ERROR_KEY_ALREADY_EXISTS.\n"); + ret = VKD3D_ERROR_KEY_ALREADY_EXISTS; + goto done; + } + + e = vkd3d_malloc(sizeof(*e)); + if (!e) + { + ret = VKD3D_ERROR_OUT_OF_MEMORY; + goto done; + } + e->payload = vkd3d_malloc(key_size + value_size); + if (!e->payload) + { + vkd3d_free(e); + ret = VKD3D_ERROR_OUT_OF_MEMORY; + goto done; + } + + e->h.key_size = key_size; + e->h.value_size = value_size; + e->h.hash = k.hash; + memcpy(e->payload, key, key_size); + memcpy(e->payload + key_size, value, value_size); + + vkd3d_shader_cache_add_item(cache, e); + TRACE("Cache item %#"PRIx64" stored.\n", k.hash); + ret = VKD3D_OK; + +done: + return ret; +} diff --git a/libs/vkd3d/device.c b/libs/vkd3d/device.c index 90de27c53..4f88f6e6f 100644 --- a/libs/vkd3d/device.c +++ b/libs/vkd3d/device.c @@ -2674,10 +2674,20 @@ static HRESULT STDMETHODCALLTYPE d3d12_cache_session_FindValue(ID3D12ShaderCache static HRESULT STDMETHODCALLTYPE d3d12_cache_session_StoreValue(ID3D12ShaderCacheSession *iface, const void *key, UINT key_size, const void *value, UINT value_size) { - FIXME("iface %p, key %p, key_size %#x, value %p, value_size %u stub!\n", iface, key, key_size, - value, value_size); + struct d3d12_cache_session *session = impl_from_ID3D12ShaderCacheSession(iface); + enum vkd3d_result ret;
- return E_NOTIMPL; + TRACE("iface %p, key %p, key_size %#x, value %p, value_size %u.\n", + iface, key, key_size, value, value_size); + + if (!key || !key_size || !value || !value_size) + { + WARN("Invalid input parameters, returning E_INVALIDARG.\n"); + return E_INVALIDARG; + } + + ret = vkd3d_shader_cache_put(session->cache, key, key_size, value, value_size); + return hresult_from_vkd3d_result(ret); }
static void STDMETHODCALLTYPE d3d12_cache_session_SetDeleteOnDestroy(ID3D12ShaderCacheSession *iface) diff --git a/libs/vkd3d/vkd3d_private.h b/libs/vkd3d/vkd3d_private.h index 91ffa6e98..9d8658608 100644 --- a/libs/vkd3d/vkd3d_private.h +++ b/libs/vkd3d/vkd3d_private.h @@ -1932,5 +1932,7 @@ struct vkd3d_shader_cache; int vkd3d_shader_open_cache(struct vkd3d_shader_cache **cache); unsigned int vkd3d_shader_cache_incref(struct vkd3d_shader_cache *cache); unsigned int vkd3d_shader_cache_decref(struct vkd3d_shader_cache *cache); +int vkd3d_shader_cache_put(struct vkd3d_shader_cache *cache, + const void *key, size_t key_size, const void *value, size_t value_size);
#endif /* __VKD3D_PRIVATE_H */