cache: Refactor lock strategy of internal hashmaps.

Rather than having to take writer lock on serialize calls from the
outside, we should just take locks when accessing the internal hashmaps
instead.

Signed-off-by: Hans-Kristian Arntzen <post@arntzen-software.no>
This commit is contained in:
Hans-Kristian Arntzen 2022-02-02 15:08:10 +01:00
parent 7c228139c3
commit 18a5315db4
2 changed files with 114 additions and 19 deletions

View File

@ -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,

View File

@ -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;