From: Stefan Dösinger stefan@codeweavers.com
---
Q1: Can I add those include files to vkd3d.h or is there a problem?
Why not in vkd3d-shader? Because vkd3d-shader has no locks/mutexes, and I'd like to do locking in the shader cache implementation instead of the caller.
Q2: Since this is not in vkd3d-shader I could use DXGI_ERROR_* instead of defining more vkd3d_result enums. I kinda like having this independent of dxgi types though. --- Makefile.am | 1 + include/vkd3d.h | 208 ++++++++++++++++++++++++++++++++++++++++++ include/vkd3d_types.h | 10 ++ libs/vkd3d/cache.c | 60 ++++++++++++ libs/vkd3d/vkd3d.map | 6 ++ 5 files changed, 285 insertions(+) create mode 100644 libs/vkd3d/cache.c
diff --git a/Makefile.am b/Makefile.am index bc648b631..0c135b72e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -332,6 +332,7 @@ libvkd3d_la_SOURCES = \ include/vkd3d_d3d12.idl \ include/vkd3d_d3dcommon.idl \ include/vkd3d_unknown.idl \ + libs/vkd3d/cache.c \ libs/vkd3d/command.c \ libs/vkd3d/device.c \ libs/vkd3d/resource.c \ diff --git a/include/vkd3d.h b/include/vkd3d.h index a3bb8e0dd..dd98bf34f 100644 --- a/include/vkd3d.h +++ b/include/vkd3d.h @@ -19,6 +19,8 @@ #ifndef __VKD3D_H #define __VKD3D_H
+#include <stdbool.h> +#include <stdint.h> #include <vkd3d_types.h>
#ifndef VKD3D_NO_WIN32_TYPES @@ -187,6 +189,72 @@ struct vkd3d_image_resource_create_info D3D12_RESOURCE_STATES present_state; };
+struct vkd3d_shader_cache; + +/** The output format of a compiled shader. */ +enum vkd3d_shader_cache_flags +{ + /** + * No particular behaviour modifications. + */ + VKD3D_SHADER_CACHE_FLAGS_NONE = 0x00000000, + /** + * Don't acquire the cache mutex before access. + */ + VKD3D_SHADER_CACHE_FLAGS_NO_SERIALIZE = 0x00000001, + /** + * Don't allow modifications, don't serialize back to a file. + */ + VKD3D_SHADER_CACHE_FLAGS_READ_ONLY = 0x00000002, + /** + * A memory only cache that is initially empty and gets discarded on close. + */ + VKD3D_SHADER_CACHE_FLAGS_MEMORY_ONLY = 0x00000004, + + VKD3D_FORCE_32_BIT_ENUM(VKD3D_SHADER_CACHE_FLAGS), +}; + +/** + * Huhu document me + * + * \since 1.10 + */ +struct vkd3d_shader_cache_desc +{ + /** Maximum amount of data the cache holds in memory. */ + uint32_t mem_size; + /** Maximum amount of data written to disk. Ignored for VKD3D_SHADER_CACHE_MEMORY_ONLY. */ + uint32_t disk_size; + /** Maximum number of cache entries. */ + uint32_t max_entries; + /** Random flags, what else. */ + enum vkd3d_shader_cache_flags flags; + /** An application-chosen version number. If the version of an existing + * cache on disk does not match, the old data will be discarded. */ + uint64_t version; +}; + +/** + * Callback function for vkd3d_shader_cache_enumerate. + * + * \ref key and \ref value become invalid after the callback returns and must not be freed or modified. + * + * \param key The application-specified key of the currently enumerated element. + * + * \param key_size Size of \ref key in bytes. + * + * \param value The value associated with \ref key. + * + * \param value_size Size of \ref value in bytes. + * + * \param context The context parameter passed to \ref vkd3d_shader_cache_enumerate. + * + * \return true if the enumeration should be continued, false to abort it. + */ +typedef bool (vkd3d_shader_cache_traverse_func)(struct vkd3d_shader_cache *cache, + const void *key, uint32_t key_size, const void *value, + uint32_t value_size, void *context); + #ifdef LIBVKD3D_SOURCE # define VKD3D_API VKD3D_EXPORT #else @@ -282,6 +350,124 @@ VKD3D_API HRESULT vkd3d_create_versioned_root_signature_deserializer(const void */ VKD3D_API void vkd3d_set_log_callback(PFN_vkd3d_log callback);
+/** + * Creates a new shader cache or opens an existing one. + * + * \param name The name of the cache. In case of an on-disk cache, this is a file name. In case of a + * memory-only cache, opening the same name again in the same process will return the same + * vkd3d_shader_cache handle. + * Cache handles are reference counted, so vkd3d_shader_cache_close has to be called for each + * successful vkd3d_shader_cache_open invocation. + * + * \param desc Cache properties. See \ref vkd3d_shader_cache_desc. + * + * \param cache Return pointer of the opened or created cache. + * + * \return A member of \ref vkd3d_result. + * + * \since 1.10 + */ +VKD3D_API int vkd3d_shader_cache_open(const char *name, + const struct vkd3d_shader_cache_desc *desc, struct vkd3d_shader_cache **cache); + +/** + * Decrements the cache reference count, closing it if it falls to zero. + * + * \param cache The cache to close. + * + * \since 1.10 + */ +VKD3D_API void vkd3d_shader_cache_close(struct vkd3d_shader_cache *cache); + +/** + * Stores a key-value pair in a shader cache. + * + * \param cache The cache to store the value in. + * + * \param key An opaque key of key_size bytes. The cache does not parse the key in any way. If the + * key already exists, the existing value will be replaced. + * FIXME: For some users (e.g. the renderpass cache) it would be interesting to prevent replacement + * and get an error instead if the value already exists. Without this they need their own lock to + * have an atomic get() - create new object - put() sequence. + * + * \param key_size The size of \ref key in bytes. + * + * \param value The value to associate with \ref key. + * + * \param value_size The size of \ref value in bytes. + * + * \return A member of \ref vkd3d_result. + * + * \since 1.10 + */ +VKD3D_API int vkd3d_shader_cache_put(struct vkd3d_shader_cache *cache, + const void *key, uint32_t key_size, const void *value, uint32_t value_size); + +/** + * Retrieves the stored value associated with a key in a shader cache. + * + * If the key is found, \ref value_size is set to the size of the value stored in the cache. If + * \ref value is non-NULL, and the input value of \ref value_size is equal to or larger than the + * size of the stored value, the stored value will be copied to the memory pointed to by \ref value. + * + * \param cache The cache to retrieve the value from. + * + * \param key The key to look up. + * + * \param key_size The size of \ref key in bytes. + * + * \param value The buffer where to write the value to, of size *value_size. This parameter may be + * NULL. + * + * \param value_size The size of \ref value in bytes. The size of the stored value will be returned + * here. + * + * \return A member of \ref vkd3d_result. + * + * \since 1.10 + */ +VKD3D_API int vkd3d_shader_cache_get(struct vkd3d_shader_cache *cache, + const void *key, uint32_t key_size, void *value, uint32_t *value_size); + +/** + * Marks an on-disk shader cache for deletion. + * + * When the final reference of \ref cache is released, the cache files on disk will be deleted. This + * function has no effect on memory-only caches, which are discarded after use in any case. + * + * \param cache The cache to delete. + * + * \since 1.10 + */ +VKD3D_API void vkd3d_shader_cache_delete_on_destroy(struct vkd3d_shader_cache *cache); + +/** + * Enumerates all key-value pairs in a cache. + * + * This function invokes \ref cb once for each stored entry. No particular enumeration order is + * guaranteed. + * The cache's lock is held during the entire operation, including when invoking the callback. + * + * This function does not make any guarantees about the order of enumeration. + * + * FIXME: Should calling vkd3d_shader_cache_get or vkd3d_shader_cache_put on the same cache from the + * callback be allowed? I am inclined to say yes to _get, but not _put. In either case we need + * reentrant locks. _put that doesn't add or remove keys (just overwrites values) would be ok too, + * but that is harder to spell out. + * + * If we decide "no calls to _get", remove the \ref cache parameter from vkd3d_shader_cache_traverse_func. + * + * \param cache The cache which contents should be enumerated. + * + * \param cb callback function, see \ref vkd3d_shader_cache_traverse_func. + + * \param context An application-specified pointer that is passed to the callback for each invocation. + * + * \since 1.10 + */ +VKD3D_API void vkd3d_shader_cache_enumerate(struct vkd3d_shader_cache *cache, + vkd3d_shader_cache_traverse_func *cb, void *context); + #endif /* VKD3D_NO_PROTOTYPES */
/* @@ -328,6 +514,28 @@ typedef HRESULT (*PFN_vkd3d_create_versioned_root_signature_deserializer)(const /** Type of vkd3d_set_log_callback(). \since 1.4 */ typedef void (*PFN_vkd3d_set_log_callback)(PFN_vkd3d_log callback);
+/** Type of vkd3d_shader_cache_open(). \since 1.10 */ +typedef int (*PFN_vkd3d_shader_cache_open)(const char *name, + const struct vkd3d_shader_cache_desc *desc, struct vkd3d_shader_cache **cache); + +/** Type of vkd3d_shader_cache_close(). \since 1.10 */ +typedef void (*PFN_vkd3d_shader_cache_close)(struct vkd3d_shader_cache *cache); + +/** Type of vkd3d_shader_cache_put(). \since 1.10 */ +typedef int (*PFN_vkd3d_shader_cache_put)(struct vkd3d_shader_cache *cache, + const void *key, uint32_t key_size, const void *value, uint32_t value_size); + +/** Type of vkd3d_shader_cache_get(). \since 1.10 */ +typedef int (*PFN_vkd3d_shader_cache_get)(struct vkd3d_shader_cache *cache, + const void *key, uint32_t key_size, void *value, uint32_t *value_size); + +/** Type of vkd3d_shader_cache_delete_on_destroy(). \since 1.10 */ +typedef void (*PFN_vkd3d_shader_cache_delete_on_destroy)(struct vkd3d_shader_cache *cache); + +/** Type of vkd3d_shader_cache_enumerate(). \since 1.10 */ +typedef void (*PFN_vkd3d_shader_cache_enumerate)(struct vkd3d_shader_cache *cache, + vkd3d_shader_cache_traverse_func *cb, void *context); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/include/vkd3d_types.h b/include/vkd3d_types.h index 4a7aca236..041f2fed4 100644 --- a/include/vkd3d_types.h +++ b/include/vkd3d_types.h @@ -51,6 +51,16 @@ enum vkd3d_result VKD3D_ERROR_INVALID_SHADER = -4, /** The operation is not implemented in this version of vkd3d. */ VKD3D_ERROR_NOT_IMPLEMENTED = -5, + /** The requested shader cache key was not found. */ + VKD3D_ERROR_NOT_FOUND = -6, + /** The requested shader cache value was bigger than the passed buffer. */ + VKD3D_ERROR_MORE_DATA = -7, + /** A different key with the same hash was found in the shader cache. */ + VKD3D_ERROR_HASH_COLLISION = -8, + /** A shader cache with the same name but different version is already opened. */ + VKD3D_ERROR_VERSION_MISMATCH = -9, + /** The cache lock is contended. */ + VKD3D_ERROR_LOCK_NOT_AVAILABLE = -10,
VKD3D_FORCE_32_BIT_ENUM(VKD3D_RESULT), }; diff --git a/libs/vkd3d/cache.c b/libs/vkd3d/cache.c new file mode 100644 index 000000000..6fe12b10e --- /dev/null +++ b/libs/vkd3d/cache.c @@ -0,0 +1,60 @@ +/* + * Copyright 2024 Stefan Dösinger for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "vkd3d_private.h" +#include "rbtree.h" + +#include <stdarg.h> +#include <stdio.h> + +int vkd3d_shader_cache_open(const char *name, + const struct vkd3d_shader_cache_desc *desc, struct vkd3d_shader_cache **cache) +{ + FIXME("%s, %p, %p: stub!\n", debugstr_a(name), desc, cache); + return VKD3D_ERROR_NOT_IMPLEMENTED; +} + +void vkd3d_shader_cache_close(struct vkd3d_shader_cache *cache) +{ + FIXME("%p Stub!\n", cache); +} + +int vkd3d_shader_cache_put(struct vkd3d_shader_cache *cache, + const void *key, uint32_t key_size, const void *value, uint32_t value_size) +{ + FIXME("%p, %p, %#x, %p, %#x stub!\n", cache, key, key_size, value, value_size); + return VKD3D_ERROR_NOT_IMPLEMENTED; +} + +int vkd3d_shader_cache_get(struct vkd3d_shader_cache *cache, + const void *key, uint32_t key_size, void *value, uint32_t *value_size) +{ + FIXME("%p, %p, %#x, %p, %p stub!\n", cache, key, key_size, value, value_size); + return VKD3D_ERROR_NOT_IMPLEMENTED; +} + +void vkd3d_shader_cache_delete_on_destroy(struct vkd3d_shader_cache *cache) +{ + FIXME("%p Stub!\n", cache); +} + +void vkd3d_shader_cache_enumerate(struct vkd3d_shader_cache *cache, + vkd3d_shader_cache_traverse_func *cb, void *context) +{ + FIXME("%p, %p, %p: stub!\n", cache, cb, context); +} diff --git a/libs/vkd3d/vkd3d.map b/libs/vkd3d/vkd3d.map index 441b2e35b..9e7bdbe9e 100644 --- a/libs/vkd3d/vkd3d.map +++ b/libs/vkd3d/vkd3d.map @@ -23,6 +23,12 @@ global: vkd3d_serialize_root_signature; vkd3d_serialize_versioned_root_signature; vkd3d_set_log_callback; + vkd3d_shader_cache_close; + vkd3d_shader_cache_delete_on_destroy; + vkd3d_shader_cache_enumerate; + vkd3d_shader_cache_get; + vkd3d_shader_cache_open; + vkd3d_shader_cache_put;
local: *; };