diff --git a/libs/vkd3d/cache.c b/libs/vkd3d/cache.c index c638db66..28f26c2c 100644 --- a/libs/vkd3d/cache.c +++ b/libs/vkd3d/cache.c @@ -556,7 +556,7 @@ static uint32_t d3d12_cached_pipeline_entry_name_table_size(const struct vkd3d_c return sizeof(entry->key.internal_key_hash); } -static bool d3d12_pipeline_library_insert_hash_map_blob(struct d3d12_pipeline_library *pipeline_library, +static bool d3d12_pipeline_library_insert_hash_map_blob_locked(struct d3d12_pipeline_library *pipeline_library, struct hash_map *map, const struct vkd3d_cached_pipeline_entry *entry) { const struct vkd3d_cached_pipeline_entry *new_entry; @@ -571,6 +571,48 @@ static bool d3d12_pipeline_library_insert_hash_map_blob(struct d3d12_pipeline_li return false; } +static bool d3d12_pipeline_library_insert_hash_map_blob_internal(struct d3d12_pipeline_library *pipeline_library, + struct hash_map *map, const struct vkd3d_cached_pipeline_entry *entry) +{ + /* Used for internal hashmap updates. We expect reasonable amount of duplicates, + * prefer read -> write promotion. */ + const struct vkd3d_cached_pipeline_entry *new_entry; + bool ret; + int rc; + + if ((rc = rwlock_lock_read(&pipeline_library->internal_hashmap_mutex))) + { + ERR("Failed to lock mutex, rc %d.\n", rc); + return false; + } + + if (hash_map_find(map, &entry->key)) + { + rwlock_unlock_read(&pipeline_library->internal_hashmap_mutex); + return false; + } + + rwlock_unlock_read(&pipeline_library->internal_hashmap_mutex); + if ((rc = rwlock_lock_write(&pipeline_library->internal_hashmap_mutex))) + { + ERR("Failed to lock mutex, rc %d.\n", rc); + return false; + } + + if ((new_entry = (const struct vkd3d_cached_pipeline_entry*)hash_map_insert(map, &entry->key, &entry->entry)) && + new_entry->data.blob == entry->data.blob) + { + pipeline_library->total_name_table_size += d3d12_cached_pipeline_entry_name_table_size(entry); + pipeline_library->total_blob_size += align(entry->data.blob_length, VKD3D_PIPELINE_BLOB_ALIGN); + ret = true; + } + else + ret = false; + + rwlock_unlock_write(&pipeline_library->internal_hashmap_mutex); + return ret; +} + static size_t vkd3d_shader_code_compute_serialized_size(const struct vkd3d_shader_code *code, size_t *out_varint_size, bool inline_spirv) { @@ -665,7 +707,7 @@ static void vkd3d_shader_code_serialize_referenced(struct d3d12_pipeline_library internal->checksum = vkd3d_pipeline_blob_compute_data_checksum(internal->data, wrapped_varint_size); /* For duplicate, we won't insert. Just free the blob. */ - if (!d3d12_pipeline_library_insert_hash_map_blob(pipeline_library, + if (!d3d12_pipeline_library_insert_hash_map_blob_internal(pipeline_library, &pipeline_library->spirv_cache_map, &entry)) { vkd3d_free(internal); @@ -786,7 +828,7 @@ static VkResult vkd3d_serialize_pipeline_state_referenced(struct d3d12_pipeline_ internal->checksum = vkd3d_pipeline_blob_compute_data_checksum(blob.code, blob.size); /* For duplicate, we won't insert. Just free the blob. */ - if (!d3d12_pipeline_library_insert_hash_map_blob(pipeline_library, + if (!d3d12_pipeline_library_insert_hash_map_blob_internal(pipeline_library, &pipeline_library->driver_cache_map, &entry)) { vkd3d_free(internal); @@ -1071,6 +1113,7 @@ static void d3d12_pipeline_library_cleanup(struct d3d12_pipeline_library *pipeli vkd3d_private_store_destroy(&pipeline_library->private_store); rwlock_destroy(&pipeline_library->mutex); + rwlock_destroy(&pipeline_library->internal_hashmap_mutex); } static HRESULT STDMETHODCALLTYPE d3d12_pipeline_library_QueryInterface(d3d12_pipeline_library_iface *iface, @@ -1172,6 +1215,7 @@ static HRESULT STDMETHODCALLTYPE d3d12_pipeline_library_StorePipeline(d3d12_pipe struct vkd3d_cached_pipeline_entry entry; void *new_name, *new_blob; VkResult vr; + HRESULT hr; int rc; TRACE("iface %p, name %s, pipeline %p.\n", iface, debugstr_w(name), pipeline); @@ -1179,7 +1223,7 @@ static HRESULT STDMETHODCALLTYPE d3d12_pipeline_library_StorePipeline(d3d12_pipe if (vkd3d_config_flags & VKD3D_CONFIG_FLAG_PIPELINE_LIBRARY_LOG) INFO("Serializing pipeline to library.\n"); - if ((rc = rwlock_lock_write(&pipeline_library->mutex))) + if ((rc = rwlock_lock_read(&pipeline_library->mutex))) { ERR("Failed to lock mutex, rc %d.\n", rc); return hresult_from_errno(rc); @@ -1192,14 +1236,14 @@ static HRESULT STDMETHODCALLTYPE d3d12_pipeline_library_StorePipeline(d3d12_pipe if (hash_map_find(&pipeline_library->pso_map, &entry.key)) { WARN("Pipeline %s already exists.\n", debugstr_w(name)); - rwlock_unlock_write(&pipeline_library->mutex); + rwlock_unlock_read(&pipeline_library->mutex); return E_INVALIDARG; } /* We need to allocate persistent storage for the name */ if (!(new_name = malloc(entry.key.name_length))) { - rwlock_unlock_write(&pipeline_library->mutex); + rwlock_unlock_read(&pipeline_library->mutex); return E_OUTOFMEMORY; } @@ -1209,14 +1253,14 @@ static HRESULT STDMETHODCALLTYPE d3d12_pipeline_library_StorePipeline(d3d12_pipe if (FAILED(vr = vkd3d_serialize_pipeline_state(pipeline_library, pipeline_state, &entry.data.blob_length, NULL))) { vkd3d_free(new_name); - rwlock_unlock_write(&pipeline_library->mutex); + rwlock_unlock_read(&pipeline_library->mutex); return hresult_from_vk_result(vr); } if (!(new_blob = malloc(entry.data.blob_length))) { vkd3d_free(new_name); - rwlock_unlock_write(&pipeline_library->mutex); + rwlock_unlock_read(&pipeline_library->mutex); return E_OUTOFMEMORY; } @@ -1224,28 +1268,53 @@ static HRESULT STDMETHODCALLTYPE d3d12_pipeline_library_StorePipeline(d3d12_pipe { vkd3d_free(new_name); vkd3d_free(new_blob); - rwlock_unlock_write(&pipeline_library->mutex); + rwlock_unlock_read(&pipeline_library->mutex); return hresult_from_vk_result(vr); } + rwlock_unlock_read(&pipeline_library->mutex); + entry.data.blob = new_blob; entry.data.is_new = 1; entry.data.state = pipeline_state; - if (!d3d12_pipeline_library_insert_hash_map_blob(pipeline_library, &pipeline_library->pso_map, &entry)) + /* Now is the time to promote to a writer lock. */ + if ((rc = rwlock_lock_write(&pipeline_library->mutex))) + { + ERR("Failed to lock mutex, rc %d.\n", rc); + vkd3d_free(new_name); + vkd3d_free(new_blob); + return hresult_from_errno(rc); + } + + /* Detected duplicate late, but be accurate in how we report this. */ + if (hash_map_find(&pipeline_library->pso_map, &entry.key)) + { + WARN("Pipeline %s already exists.\n", debugstr_w(name)); + hr = E_INVALIDARG; + } + else if (!d3d12_pipeline_library_insert_hash_map_blob_locked(pipeline_library, &pipeline_library->pso_map, &entry)) + { + /* This path shouldn't happen unless there are OOM scenarios. */ + hr = E_OUTOFMEMORY; + } + else + { + /* If we get a subsequent LoadLibrary, we have to hand it back out again. + * API tests inform us that we need internal ref-count here. */ + d3d12_pipeline_state_inc_ref(pipeline_state); + hr = S_OK; + } + + rwlock_unlock_write(&pipeline_library->mutex); + + if (FAILED(hr)) { vkd3d_free(new_name); vkd3d_free(new_blob); - rwlock_unlock_write(&pipeline_library->mutex); - return E_OUTOFMEMORY; } - /* If we get a subsequent LoadLibrary, we have to hand it back out again. - * API tests inform us that we need internal ref-count here. */ - d3d12_pipeline_state_inc_ref(pipeline_state); - - rwlock_unlock_write(&pipeline_library->mutex); - return S_OK; + return hr; } static HRESULT d3d12_pipeline_library_load_pipeline(struct d3d12_pipeline_library *pipeline_library, LPCWSTR name, @@ -1437,9 +1506,17 @@ static SIZE_T STDMETHODCALLTYPE d3d12_pipeline_library_GetSerializedSize(d3d12_p return 0; } + if ((rc = rwlock_lock_read(&pipeline_library->internal_hashmap_mutex))) + { + ERR("Failed to lock mutex, rc %d.\n", rc); + rwlock_unlock_read(&pipeline_library->mutex); + return 0; + } + total_size = d3d12_pipeline_library_get_serialized_size(pipeline_library); rwlock_unlock_read(&pipeline_library->mutex); + rwlock_unlock_read(&pipeline_library->internal_hashmap_mutex); return total_size; } @@ -1555,8 +1632,16 @@ static HRESULT STDMETHODCALLTYPE d3d12_pipeline_library_Serialize(d3d12_pipeline return E_FAIL; } + if ((rc = rwlock_lock_read(&pipeline_library->internal_hashmap_mutex))) + { + ERR("Failed to lock mutex, rc %d.\n", rc); + rwlock_unlock_read(&pipeline_library->mutex); + return E_FAIL; + } + hr = d3d12_pipeline_library_serialize(pipeline_library, data, data_size); rwlock_unlock_read(&pipeline_library->mutex); + rwlock_unlock_read(&pipeline_library->internal_hashmap_mutex); return hr; } @@ -1657,7 +1742,7 @@ static HRESULT d3d12_pipeline_library_unserialize_hash_map( entry.data.is_new = 0; entry.data.state = NULL; - if (!d3d12_pipeline_library_insert_hash_map_blob(pipeline_library, map, &entry)) + if (!d3d12_pipeline_library_insert_hash_map_blob_locked(pipeline_library, map, &entry)) return E_OUTOFMEMORY; } @@ -1786,6 +1871,12 @@ static HRESULT d3d12_pipeline_library_init(struct d3d12_pipeline_library *pipeli if ((rc = rwlock_init(&pipeline_library->mutex))) return hresult_from_errno(rc); + if ((rc = rwlock_init(&pipeline_library->internal_hashmap_mutex))) + { + rwlock_destroy(&pipeline_library->mutex); + return hresult_from_errno(rc); + } + hash_map_init(&pipeline_library->spirv_cache_map, vkd3d_cached_pipeline_hash_internal, vkd3d_cached_pipeline_compare_internal, sizeof(struct vkd3d_cached_pipeline_entry)); hash_map_init(&pipeline_library->driver_cache_map, vkd3d_cached_pipeline_hash_internal, diff --git a/libs/vkd3d/vkd3d_private.h b/libs/vkd3d/vkd3d_private.h index 412c3611..6cafc502 100644 --- a/libs/vkd3d/vkd3d_private.h +++ b/libs/vkd3d/vkd3d_private.h @@ -1651,6 +1651,10 @@ struct d3d12_pipeline_library struct d3d12_device *device; rwlock_t mutex; + /* driver_cache_map and spirv_cache_map can be touched in serialize_pipeline_state. + * Use the internal mutex when touching the internal caches + * so we don't need a big lock on the outside when serializing. */ + rwlock_t internal_hashmap_mutex; struct hash_map pso_map; struct hash_map driver_cache_map; struct hash_map spirv_cache_map;