Skip to content

Commit 6594a63

Browse files
committed
Merge pull request #100213 from DarioSamo/pipeline-hash-map-thread-safety
Improve thread-safety of pipeline hash map.
2 parents c58fbaf + be1dce1 commit 6594a63

File tree

3 files changed

+30
-27
lines changed

3 files changed

+30
-27
lines changed

servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -4690,10 +4690,10 @@ void RenderForwardClustered::mesh_generate_pipelines(RID p_mesh, bool p_backgrou
46904690
_mesh_compile_pipelines_for_surface(surface, global_pipeline_data_required, RS::PIPELINE_SOURCE_MESH, &pipeline_pairs);
46914691
}
46924692

4693-
// Try to retrieve all the pipeline pairs that were compiled. This will force the loader to wait on all ubershader pipelines to be ready.
4693+
// Wait for all the pipelines that were compiled. This will force the loader to wait on all ubershader pipelines to be ready.
46944694
if (!p_background_compilation && !pipeline_pairs.is_empty()) {
46954695
for (ShaderPipelinePair pair : pipeline_pairs) {
4696-
pair.first->pipeline_hash_map.get_pipeline(pair.second, pair.second.hash(), true, RS::PIPELINE_SOURCE_MESH);
4696+
pair.first->pipeline_hash_map.wait_for_pipeline(pair.second.hash());
46974697
}
46984698
}
46994699
}

servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -327,10 +327,10 @@ void RenderForwardMobile::mesh_generate_pipelines(RID p_mesh, bool p_background_
327327
_mesh_compile_pipelines_for_surface(surface, global_pipeline_data_required, RS::PIPELINE_SOURCE_MESH, &pipeline_pairs);
328328
}
329329

330-
// Try to retrieve all the pipeline pairs that were compiled. This will force the loader to wait on all ubershader pipelines to be ready.
330+
// Try to wait for all the pipelines that were compiled. This will force the loader to wait on all ubershader pipelines to be ready.
331331
if (!p_background_compilation && !pipeline_pairs.is_empty()) {
332332
for (ShaderPipelinePair pair : pipeline_pairs) {
333-
pair.first->pipeline_hash_map.get_pipeline(pair.second, pair.second.hash(), true, RS::PIPELINE_SOURCE_MESH);
333+
pair.first->pipeline_hash_map.wait_for_pipeline(pair.second.hash());
334334
}
335335
}
336336
}

servers/rendering/renderer_rd/pipeline_hash_map_rd.h

+26-23
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class PipelineHashMapRD {
4646
RBMap<uint32_t, RID> hash_map;
4747
LocalVector<Pair<uint32_t, RID>> compiled_queue;
4848
Mutex compiled_queue_mutex;
49+
RBSet<uint32_t> compilation_set;
4950
HashMap<uint32_t, WorkerThreadPool::TaskID> compilation_tasks;
5051
Mutex local_mutex;
5152

@@ -76,7 +77,7 @@ class PipelineHashMapRD {
7677
return !hashes_added.is_empty();
7778
}
7879

79-
void _wait_for_compilation() {
80+
void _wait_for_all_pipelines() {
8081
MutexLock local_lock(local_mutex);
8182
for (KeyValue<uint32_t, WorkerThreadPool::TaskID> key_value : compilation_tasks) {
8283
WorkerThreadPool::get_singleton()->wait_for_task_completion(key_value.value);
@@ -91,23 +92,18 @@ class PipelineHashMapRD {
9192
}
9293

9394
// Start compilation of a pipeline ahead of time in the background. Returns true if the compilation was started, false if it wasn't required. Source is only used for collecting statistics.
94-
bool compile_pipeline(const Key &p_key, uint32_t p_key_hash, RS::PipelineSource p_source) {
95+
void compile_pipeline(const Key &p_key, uint32_t p_key_hash, RS::PipelineSource p_source) {
9596
DEV_ASSERT((creation_object != nullptr) && (creation_function != nullptr) && "Creation object and function was not set before attempting to compile a pipeline.");
9697

97-
// Check if the pipeline was already compiled.
98-
compiled_queue_mutex.lock();
99-
bool already_exists = hash_map.has(p_key_hash);
100-
compiled_queue_mutex.unlock();
101-
if (already_exists) {
102-
return false;
103-
}
104-
105-
// Check if the pipeline is currently being compiled.
10698
MutexLock local_lock(local_mutex);
107-
if (compilation_tasks.has(p_key_hash)) {
108-
return false;
99+
if (compilation_set.has(p_key_hash)) {
100+
// Check if the pipeline was already submitted.
101+
return;
109102
}
110103

104+
// Record the pipeline as submitted, a task can't be started for it again.
105+
compilation_set.insert(p_key_hash);
106+
111107
if (compilations_mutex != nullptr) {
112108
MutexLock compilations_lock(*compilations_mutex);
113109
compilations[p_source]++;
@@ -139,8 +135,21 @@ class PipelineHashMapRD {
139135
// Queue a background compilation task.
140136
WorkerThreadPool::TaskID task_id = WorkerThreadPool::get_singleton()->add_template_task(creation_object, creation_function, p_key, false, "PipelineCompilation");
141137
compilation_tasks.insert(p_key_hash, task_id);
138+
}
139+
140+
void wait_for_pipeline(uint32_t p_key_hash) {
141+
MutexLock local_lock(local_mutex);
142+
if (!compilation_set.has(p_key_hash)) {
143+
// The pipeline was never submitted, we can't wait for it.
144+
return;
145+
}
142146

143-
return true;
147+
HashMap<uint32_t, WorkerThreadPool::TaskID>::Iterator task_it = compilation_tasks.find(p_key_hash);
148+
if (task_it != compilation_tasks.end()) {
149+
// Wait for and remove the compilation task if it exists.
150+
WorkerThreadPool::get_singleton()->wait_for_task_completion(task_it->value);
151+
compilation_tasks.remove(task_it);
152+
}
144153
}
145154

146155
// Retrieve a pipeline. It'll return an empty pipeline if it's not available yet, but it'll be guaranteed to succeed if 'wait for compilation' is true and stall as necessary. Source is just an optional number to aid debugging.
@@ -155,18 +164,11 @@ class PipelineHashMapRD {
155164
}
156165

157166
if (e == nullptr) {
158-
// Lock access to the compilation maps.
159-
MutexLock local_lock(local_mutex);
160-
161167
// Request compilation. The method will ignore the request if it's already being compiled.
162168
compile_pipeline(p_key, p_key_hash, p_source);
163169

164170
if (p_wait_for_compilation) {
165-
if (compilation_tasks.has(p_key_hash)) {
166-
// If a background compilation task was used, wait for it.
167-
WorkerThreadPool::get_singleton()->wait_for_task_completion(compilation_tasks[p_key_hash]);
168-
}
169-
171+
wait_for_pipeline(p_key_hash);
170172
_add_new_pipelines_to_map();
171173

172174
e = hash_map.find(p_key_hash);
@@ -187,14 +189,15 @@ class PipelineHashMapRD {
187189

188190
// Delete all cached pipelines. Can stall if background compilation is in progress.
189191
void clear_pipelines() {
190-
_wait_for_compilation();
192+
_wait_for_all_pipelines();
191193
_add_new_pipelines_to_map();
192194

193195
for (KeyValue<uint32_t, RID> entry : hash_map) {
194196
RD::get_singleton()->free(entry.value);
195197
}
196198

197199
hash_map.clear();
200+
compilation_set.clear();
198201
}
199202

200203
// Set the external pipeline compilations array to increase the counters on every time a pipeline is compiled.

0 commit comments

Comments
 (0)