diff --git a/include/private/vkd3d_common.h b/include/private/vkd3d_common.h index 4b7da0a1..79682483 100644 --- a/include/private/vkd3d_common.h +++ b/include/private/vkd3d_common.h @@ -340,6 +340,11 @@ static inline int vkd3d_u32_compare(uint32_t x, uint32_t y) return (x > y) - (x < y); } +static inline int vkd3d_u64_compare(uint64_t x, uint64_t y) +{ + return (x > y) - (x < y); +} + #define VKD3D_BITMAP_SIZE(x) (((x) + 0x1f) >> 5) static inline bool bitmap_clear(uint32_t *map, unsigned int idx) diff --git a/include/vkd3d_types.h b/include/vkd3d_types.h index 9060fa08..18aa118f 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 object or entry already exists. \since 1.12 */ + 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 b8350a54..a6efedde 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 56ba6990..05b717ba 100644 --- a/libs/vkd3d/cache.c +++ b/libs/vkd3d/cache.c @@ -18,11 +18,58 @@ #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 ((ret = vkd3d_u64_compare(k->hash, e->h.hash))) + return ret; + if ((ret = vkd3d_u64_compare(k->key_size, e->h.key_size))) + return ret; + + /* Until now we have not seen an actual hash collision. 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. */ + if ((ret = memcmp(k->key, e->payload, k->key_size))) + FIXME("Actual case of a hash collision found.\n"); + return ret; +} + +static void vkd3d_shader_cache_add_entry(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 +81,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 +95,13 @@ unsigned int vkd3d_shader_cache_incref(struct vkd3d_shader_cache *cache) return refcount; } +static void vkd3d_shader_cache_destroy_entry(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 +110,72 @@ unsigned int vkd3d_shader_cache_decref(struct vkd3d_shader_cache *cache) if (refcount) return refcount; + rb_destroy(&cache->tree, vkd3d_shader_cache_destroy_entry, NULL); + vkd3d_free(cache); return 0; } + +static uint64_t vkd3d_shader_cache_hash_key(const void *key, size_t size) +{ + static const uint64_t fnv_prime = 0x00000100000001b3; + uint64_t hash = 0xcbf29ce484222325; + const uint8_t *k = key; + size_t i; + + for (i = 0; i < size; ++i) + hash = (hash ^ k[i]) * fnv_prime; + + return hash; +} + +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_entry(cache, e); + TRACE("Cache entry %#"PRIx64" stored.\n", k.hash); + ret = VKD3D_OK; + +done: + return ret; +} diff --git a/libs/vkd3d/device.c b/libs/vkd3d/device.c index 13d354a7..fe2669b2 100644 --- a/libs/vkd3d/device.c +++ b/libs/vkd3d/device.c @@ -2694,10 +2694,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 4d0c4a3c..63bfce7d 100644 --- a/libs/vkd3d/vkd3d_private.h +++ b/libs/vkd3d/vkd3d_private.h @@ -1776,5 +1776,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 */