diff --git a/libs/vkd3d/device.c b/libs/vkd3d/device.c index 4c53ce53..ff256631 100644 --- a/libs/vkd3d/device.c +++ b/libs/vkd3d/device.c @@ -2165,6 +2165,70 @@ static void vkd3d_gpu_va_allocator_cleanup(struct vkd3d_gpu_va_allocator *alloca pthread_mutex_destroy(&allocator->mutex); } +struct d3d12_device_singleton +{ + struct list entry; + LUID adapter_luid; + struct d3d12_device *device; +}; + +static pthread_once_t d3d12_device_map_mutex_init_once = PTHREAD_ONCE_INIT; +static pthread_mutex_t d3d12_device_map_mutex; +static struct list d3d12_device_map; + +static void d3d12_init_device_map_once(void) +{ + pthread_mutex_init(&d3d12_device_map_mutex, NULL); + list_init(&d3d12_device_map); +} + +static struct d3d12_device *d3d12_find_device_singleton(LUID luid) +{ + struct d3d12_device_singleton* current; + struct d3d12_device *device = NULL; + + LIST_FOR_EACH_ENTRY(current, &d3d12_device_map, struct d3d12_device_singleton, entry) + { + /* Avoid the bubble of time between no ref and death. */ + if (!vkd3d_atomic_uint32_load_explicit(¤t->device->refcount, vkd3d_memory_order_seq_cst)) + continue; + + if (!memcmp(¤t->adapter_luid, &luid, sizeof(LUID))) + return current->device; + } + + return NULL; +} + +static void d3d12_add_device_singleton(struct d3d12_device *device, LUID luid) +{ + struct d3d12_device_singleton *e; + + if (!(e = vkd3d_malloc(sizeof(*e)))) + { + ERR("Failed to register device singleton for adapter."); + return; + } + e->adapter_luid = luid; + e->device = device; + + list_add_tail(&d3d12_device_map, &e->entry); +} + +static void d3d12_remove_device_singleton(LUID luid) +{ + struct d3d12_device_singleton *current; + + LIST_FOR_EACH_ENTRY(current, &d3d12_device_map, struct d3d12_device_singleton, entry) + { + if (!memcmp(¤t->adapter_luid, &luid, sizeof(LUID))) + { + list_remove(¤t->entry); + return; + } + } +} + /* ID3D12Device */ static inline struct d3d12_device *impl_from_ID3D12Device(d3d12_device_iface *iface) { @@ -2240,6 +2304,9 @@ static ULONG STDMETHODCALLTYPE d3d12_device_Release(d3d12_device_iface *iface) if (!refcount) { + pthread_mutex_lock(&d3d12_device_map_mutex); + d3d12_remove_device_singleton(device->adapter_luid); + pthread_mutex_unlock(&d3d12_device_map_mutex); d3d12_device_destroy(device); vkd3d_free(device); } @@ -4662,12 +4729,27 @@ HRESULT d3d12_device_create(struct vkd3d_instance *instance, struct d3d12_device *object; HRESULT hr; + pthread_once(&d3d12_device_map_mutex_init_once, d3d12_init_device_map_once); + pthread_mutex_lock(&d3d12_device_map_mutex); + if ((object = d3d12_find_device_singleton(create_info->adapter_luid))) + { + TRACE("Returned existing singleton device %p.\n", object); + + d3d12_device_add_ref(*device = object); + pthread_mutex_unlock(&d3d12_device_map_mutex); + return S_OK; + } + if (!(object = vkd3d_malloc(sizeof(*object)))) + { + pthread_mutex_unlock(&d3d12_device_map_mutex); return E_OUTOFMEMORY; + } if (FAILED(hr = d3d12_device_init(object, instance, create_info))) { vkd3d_free(object); + pthread_mutex_unlock(&d3d12_device_map_mutex); return hr; } @@ -4676,11 +4758,16 @@ HRESULT d3d12_device_create(struct vkd3d_instance *instance, WARN("Feature level %#x is not supported.\n", create_info->minimum_feature_level); d3d12_device_destroy(object); vkd3d_free(object); + pthread_mutex_unlock(&d3d12_device_map_mutex); return E_INVALIDARG; } TRACE("Created device %p.\n", object); + d3d12_add_device_singleton(object, create_info->adapter_luid); + + pthread_mutex_unlock(&d3d12_device_map_mutex); + *device = object; return S_OK;