This adds the beginnings of vkd3d_shader_cache and links it up with ID3D12ShaderCacheSession. No actual data is stored yet, this will come in the next series.
-- v2: vkd3d: Implement reopening existing caches. vkd3d: Implement opening and closing shader caches. tests: Add CreateShaderCacheSession pointer clearing tests.
From: Stefan Dösinger stefan@codeweavers.com
Native's behavior is inconsistent. It sets the pointer to NULL when it fails to open an existing cache due to a version mismatch, but leaves the pointer untouched when the description fails validation. --- tests/d3d12.c | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/tests/d3d12.c b/tests/d3d12.c index ccebf5100..159f27cb5 100644 --- a/tests/d3d12.c +++ b/tests/d3d12.c @@ -38301,6 +38301,7 @@ static void test_shader_cache(void) base_refcount = get_refcount(device);
/* The description needs to be non-NULL and have at least the identifier set. */ + unk = (IUnknown *)0xdeadbeef; hr = ID3D12Device9_CreateShaderCacheSession(device, NULL, &IID_IUnknown, (void **)&unk); ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr); hr = ID3D12Device9_CreateShaderCacheSession(device, NULL, &IID_IUnknown, NULL); @@ -38311,6 +38312,7 @@ static void test_shader_cache(void) ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr); hr = ID3D12Device9_CreateShaderCacheSession(device, &desc, NULL, (void **)&unk); ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr); + ok(unk == (IUnknown *)0xdeadbeef, "Got unexpected pointer %p.\n", unk);
desc.Identifier = test_guid; desc.Mode = D3D12_SHADER_CACHE_MODE_MEMORY; @@ -38359,10 +38361,13 @@ static void test_shader_cache(void) (void **)&session); ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); ID3D12ShaderCacheSession_Release(session); + + session = (ID3D12ShaderCacheSession *)0xdeadbeef; desc.MaximumValueFileSizeBytes = 1024 * 1024 * 1024 + 1; hr = ID3D12Device9_CreateShaderCacheSession(device, &desc, &IID_ID3D12ShaderCacheSession, (void **)&session); ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr); + ok(session == (ID3D12ShaderCacheSession *)0xdeadbeef, "Got unexpected pointer %p.\n", unk);
memset(&desc, 0, sizeof(desc)); desc.Identifier = test_guid; @@ -38383,6 +38388,7 @@ static void test_shader_cache(void) desc.Identifier = test_guid; desc.Mode = D3D12_SHADER_CACHE_MODE_MEMORY; desc.Flags = 0x1245670; + session = (ID3D12ShaderCacheSession *)0xdeadbeef; hr = ID3D12Device9_CreateShaderCacheSession(device, &desc, &IID_ID3D12ShaderCacheSession, (void **)&session); ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr); @@ -38391,6 +38397,7 @@ static void test_shader_cache(void) hr = ID3D12Device9_CreateShaderCacheSession(device, &desc, &IID_ID3D12ShaderCacheSession, (void **)&session); ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr); + ok(session == (ID3D12ShaderCacheSession *)0xdeadbeef, "Got unexpected pointer %p.\n", unk);
memset(&desc, 0, sizeof(desc)); desc.Identifier = test_guid;
From: Stefan Dösinger stefan@codeweavers.com
--- Makefile.am | 1 + libs/vkd3d/cache.c | 59 ++++++++++++++++++++++++++++++++++++++ libs/vkd3d/device.c | 14 +++++++++ libs/vkd3d/vkd3d_private.h | 6 ++++ 4 files changed, 80 insertions(+) create mode 100644 libs/vkd3d/cache.c
diff --git a/Makefile.am b/Makefile.am index 39b041502..015cb4204 100644 --- a/Makefile.am +++ b/Makefile.am @@ -352,6 +352,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/libs/vkd3d/cache.c b/libs/vkd3d/cache.c new file mode 100644 index 000000000..56ba69904 --- /dev/null +++ b/libs/vkd3d/cache.c @@ -0,0 +1,59 @@ +/* + * 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" + +struct vkd3d_shader_cache +{ + unsigned int refcount; +}; + +int vkd3d_shader_open_cache(struct vkd3d_shader_cache **cache) +{ + struct vkd3d_shader_cache *object; + + TRACE("%p.\n", cache); + + object = vkd3d_malloc(sizeof(*object)); + if (!object) + return VKD3D_ERROR_OUT_OF_MEMORY; + + object->refcount = 1; + *cache = object; + + return VKD3D_OK; +} + +unsigned int vkd3d_shader_cache_incref(struct vkd3d_shader_cache *cache) +{ + unsigned int refcount = vkd3d_atomic_increment_u32(&cache->refcount); + TRACE("cache %p refcount %u.\n", cache, refcount); + return refcount; +} + +unsigned int vkd3d_shader_cache_decref(struct vkd3d_shader_cache *cache) +{ + unsigned int refcount = vkd3d_atomic_decrement_u32(&cache->refcount); + TRACE("cache %p refcount %u.\n", cache, refcount); + + if (refcount) + return refcount; + + vkd3d_free(cache); + return 0; +} diff --git a/libs/vkd3d/device.c b/libs/vkd3d/device.c index 65db8b70b..266465e00 100644 --- a/libs/vkd3d/device.c +++ b/libs/vkd3d/device.c @@ -2532,6 +2532,7 @@ struct d3d12_cache_session struct d3d12_device *device; struct vkd3d_private_store private_store; D3D12_SHADER_CACHE_SESSION_DESC desc; + struct vkd3d_shader_cache *cache; };
static inline struct d3d12_cache_session *impl_from_ID3D12ShaderCacheSession(ID3D12ShaderCacheSession *iface) @@ -2582,6 +2583,7 @@ static void d3d12_cache_session_destroy(struct d3d12_cache_session *session)
TRACE("Destroying cache session %p.\n", session);
+ vkd3d_shader_cache_decref(session->cache); vkd3d_private_store_destroy(&session->private_store); vkd3d_free(session);
@@ -2707,6 +2709,7 @@ static const struct ID3D12ShaderCacheSessionVtbl d3d12_cache_session_vtbl = static HRESULT d3d12_cache_session_init(struct d3d12_cache_session *session, struct d3d12_device *device, const D3D12_SHADER_CACHE_SESSION_DESC *desc) { + enum vkd3d_result ret; HRESULT hr;
session->ID3D12ShaderCacheSession_iface.lpVtbl = &d3d12_cache_session_vtbl; @@ -2723,6 +2726,17 @@ static HRESULT d3d12_cache_session_init(struct d3d12_cache_session *session, if (FAILED(hr = vkd3d_private_store_init(&session->private_store))) return hr;
+ if (session->desc.Mode == D3D12_SHADER_CACHE_MODE_DISK) + FIXME("Disk caches are not yet implemented.\n"); + + ret = vkd3d_shader_open_cache(&session->cache); + if (ret) + { + WARN("Failed to open shader cache.\n"); + vkd3d_private_store_destroy(&session->private_store); + return hresult_from_vkd3d_result(ret); + } + d3d12_device_add_ref(session->device = device);
return S_OK; diff --git a/libs/vkd3d/vkd3d_private.h b/libs/vkd3d/vkd3d_private.h index d5e12ee3c..91ffa6e98 100644 --- a/libs/vkd3d/vkd3d_private.h +++ b/libs/vkd3d/vkd3d_private.h @@ -1927,4 +1927,10 @@ static inline void vkd3d_prepend_struct(void *header, void *structure) vkd3d_header->next = vkd3d_structure; }
+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); + #endif /* __VKD3D_PRIVATE_H */
From: Stefan Dösinger stefan@codeweavers.com
---
I have tests that test what happens when a cache exists, has a matching version but other properties (e.g. disk vs memory) differ. The anwer is that the existing cache's properties win. Because those tests make use of D3D12_SHADER_CACHE_MODE_DISK I plan to send them after implementing disk cache support. --- include/private/vkd3d_common.h | 6 ++++ libs/vkd3d/device.c | 62 ++++++++++++++++++++++++++++++---- tests/d3d12.c | 13 ++++--- 3 files changed, 70 insertions(+), 11 deletions(-)
diff --git a/include/private/vkd3d_common.h b/include/private/vkd3d_common.h index 6a8694a79..10e2afd5d 100644 --- a/include/private/vkd3d_common.h +++ b/include/private/vkd3d_common.h @@ -438,6 +438,12 @@ struct vkd3d_mutex #endif };
+#ifdef _WIN32 +#define VKD3D_MUTEX_INITIALIZER {{NULL, -1, 0, 0, 0, 0}} +#else +#define VKD3D_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER +#endif + static inline void vkd3d_mutex_init(struct vkd3d_mutex *lock) { #ifdef _WIN32 diff --git a/libs/vkd3d/device.c b/libs/vkd3d/device.c index 266465e00..90de27c53 100644 --- a/libs/vkd3d/device.c +++ b/libs/vkd3d/device.c @@ -2529,12 +2529,17 @@ struct d3d12_cache_session ID3D12ShaderCacheSession ID3D12ShaderCacheSession_iface; unsigned int refcount;
+ struct list cache_list_entry; + struct d3d12_device *device; struct vkd3d_private_store private_store; D3D12_SHADER_CACHE_SESSION_DESC desc; struct vkd3d_shader_cache *cache; };
+static struct vkd3d_mutex cache_list_mutex = VKD3D_MUTEX_INITIALIZER; +static struct list cache_list = LIST_INIT(cache_list); + static inline struct d3d12_cache_session *impl_from_ID3D12ShaderCacheSession(ID3D12ShaderCacheSession *iface) { return CONTAINING_RECORD(iface, struct d3d12_cache_session, ID3D12ShaderCacheSession_iface); @@ -2583,6 +2588,10 @@ static void d3d12_cache_session_destroy(struct d3d12_cache_session *session)
TRACE("Destroying cache session %p.\n", session);
+ vkd3d_mutex_lock(&cache_list_mutex); + list_remove(&session->cache_list_entry); + vkd3d_mutex_unlock(&cache_list_mutex); + vkd3d_shader_cache_decref(session->cache); vkd3d_private_store_destroy(&session->private_store); vkd3d_free(session); @@ -2709,12 +2718,14 @@ static const struct ID3D12ShaderCacheSessionVtbl d3d12_cache_session_vtbl = static HRESULT d3d12_cache_session_init(struct d3d12_cache_session *session, struct d3d12_device *device, const D3D12_SHADER_CACHE_SESSION_DESC *desc) { + struct d3d12_cache_session *i; enum vkd3d_result ret; HRESULT hr;
session->ID3D12ShaderCacheSession_iface.lpVtbl = &d3d12_cache_session_vtbl; session->refcount = 1; session->desc = *desc; + session->cache = NULL;
if (!session->desc.MaximumValueFileSizeBytes) session->desc.MaximumValueFileSizeBytes = 128 * 1024 * 1024; @@ -2726,20 +2737,56 @@ static HRESULT d3d12_cache_session_init(struct d3d12_cache_session *session, if (FAILED(hr = vkd3d_private_store_init(&session->private_store))) return hr;
- if (session->desc.Mode == D3D12_SHADER_CACHE_MODE_DISK) - FIXME("Disk caches are not yet implemented.\n"); + vkd3d_mutex_lock(&cache_list_mutex);
- ret = vkd3d_shader_open_cache(&session->cache); - if (ret) + /* We expect the number of open caches to be small. */ + LIST_FOR_EACH_ENTRY(i, &cache_list, struct d3d12_cache_session, cache_list_entry) { - WARN("Failed to open shader cache.\n"); - vkd3d_private_store_destroy(&session->private_store); - return hresult_from_vkd3d_result(ret); + if (!memcmp(&i->desc.Identifier, &desc->Identifier, sizeof(desc->Identifier))) + { + TRACE("Found an existing cache %p from session %p.\n", i->cache, i); + if (desc->Version == i->desc.Version) + { + session->desc = i->desc; + vkd3d_shader_cache_incref(session->cache = i->cache); + break; + } + else + { + WARN("version mismatch: Existing %"PRIu64" new %"PRIu64".\n", + i->desc.Version, desc->Version); + hr = DXGI_ERROR_ALREADY_EXISTS; + goto error; + } + } }
+ if (!session->cache) + { + if (session->desc.Mode == D3D12_SHADER_CACHE_MODE_DISK) + FIXME("Disk caches are not yet implemented.\n"); + + ret = vkd3d_shader_open_cache(&session->cache); + if (ret) + { + WARN("Failed to open shader cache.\n"); + hr = hresult_from_vkd3d_result(ret); + goto error; + } + } + + /* Add it to the list even if we reused an existing cache. The other session might be destroyed, + * but the cache stays alive and can be opened a third time. */ + list_add_tail(&cache_list, &session->cache_list_entry); d3d12_device_add_ref(session->device = device);
+ vkd3d_mutex_unlock(&cache_list_mutex); return S_OK; + +error: + vkd3d_private_store_destroy(&session->private_store); + vkd3d_mutex_unlock(&cache_list_mutex); + return hr; }
/* ID3D12Device */ @@ -4888,6 +4935,7 @@ static HRESULT STDMETHODCALLTYPE d3d12_device_CreateShaderCacheSession(ID3D12Dev WARN("No output pointer, returning S_FALSE.\n"); return S_FALSE; } + *session = NULL;
if (!(object = vkd3d_malloc(sizeof(*object)))) return E_OUTOFMEMORY; diff --git a/tests/d3d12.c b/tests/d3d12.c index 159f27cb5..a120db6a5 100644 --- a/tests/d3d12.c +++ b/tests/d3d12.c @@ -38440,10 +38440,8 @@ static void test_shader_cache(void) session2 = (void *)0xdeadbeef; hr = ID3D12Device9_CreateShaderCacheSession(device, &desc, &IID_ID3D12ShaderCacheSession, (void **)&session2); - todo ok(hr == DXGI_ERROR_ALREADY_EXISTS, "Got unexpected hr %#x.\n", hr); - todo ok(!session2, "Got unexpected pointer %p.\n", session2); - if (session2) - ID3D12ShaderCacheSession_Release(session2); + ok(hr == DXGI_ERROR_ALREADY_EXISTS, "Got unexpected hr %#x.\n", hr); + ok(!session2, "Got unexpected pointer %p.\n", session2); hr = ID3D12Device9_CreateShaderCacheSession(device, &desc, &IID_IUnknown, NULL); ok(hr == S_FALSE, "NULL outptr: Got hr %#x.\n", hr);
@@ -38452,9 +38450,16 @@ static void test_shader_cache(void) refcount = get_refcount(device); ok(refcount == base_refcount, "Got unexpected refcount %u.\n", refcount);
+ /* Create two sessions with the same cache GUID. */ hr = ID3D12Device9_CreateShaderCacheSession(device, &desc, &IID_ID3D12ShaderCacheSession, (void **)&session); ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + hr = ID3D12Device9_CreateShaderCacheSession(device, &desc, + &IID_ID3D12ShaderCacheSession, (void **)&session2); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + ok(session2 != session, "Expected different interface pointers, got %p for both.\n", + session); + ID3D12ShaderCacheSession_Release(session2); ID3D12ShaderCacheSession_Release(session);
ID3D12Device9_Release(device);
This merge request was approved by Henri Verbeet.