Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/servers/rendering/renderer_rd/pipeline_hash_map_rd.h
21504 views
1
/**************************************************************************/
2
/* pipeline_hash_map_rd.h */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#pragma once
32
33
#include "core/templates/rb_set.h"
34
#include "servers/rendering/rendering_device.h"
35
#include "servers/rendering/rendering_server.h"
36
37
#define PRINT_PIPELINE_COMPILATION_KEYS 0
38
39
template <typename Key, typename CreationClass, typename CreationFunction>
40
class PipelineHashMapRD {
41
private:
42
CreationClass *creation_object = nullptr;
43
CreationFunction creation_function = nullptr;
44
Mutex *compilations_mutex = nullptr;
45
uint32_t *compilations = nullptr;
46
RBMap<uint32_t, RID> hash_map;
47
LocalVector<Pair<uint32_t, RID>> compiled_queue;
48
Mutex compiled_queue_mutex;
49
RBSet<uint32_t> compilation_set;
50
HashMap<uint32_t, WorkerThreadPool::TaskID> compilation_tasks;
51
Mutex local_mutex;
52
53
bool _add_new_pipelines_to_map() {
54
thread_local Vector<uint32_t> hashes_added;
55
hashes_added.clear();
56
57
{
58
MutexLock lock(compiled_queue_mutex);
59
for (const Pair<uint32_t, RID> &pair : compiled_queue) {
60
hash_map[pair.first] = pair.second;
61
hashes_added.push_back(pair.first);
62
}
63
64
compiled_queue.clear();
65
}
66
67
{
68
MutexLock local_lock(local_mutex);
69
for (uint32_t hash : hashes_added) {
70
HashMap<uint32_t, WorkerThreadPool::TaskID>::Iterator task_it = compilation_tasks.find(hash);
71
if (task_it != compilation_tasks.end()) {
72
compilation_tasks.remove(task_it);
73
}
74
}
75
}
76
77
return !hashes_added.is_empty();
78
}
79
80
void _wait_for_all_pipelines() {
81
thread_local LocalVector<WorkerThreadPool::TaskID> tasks_to_wait;
82
tasks_to_wait.clear();
83
{
84
MutexLock local_lock(local_mutex);
85
for (KeyValue<uint32_t, WorkerThreadPool::TaskID> key_value : compilation_tasks) {
86
tasks_to_wait.push_back(key_value.value);
87
}
88
}
89
90
for (WorkerThreadPool::TaskID task_id : tasks_to_wait) {
91
WorkerThreadPool::get_singleton()->wait_for_task_completion(task_id);
92
}
93
}
94
95
public:
96
void add_compiled_pipeline(uint32_t p_hash, RID p_pipeline) {
97
compiled_queue_mutex.lock();
98
compiled_queue.push_back({ p_hash, p_pipeline });
99
compiled_queue_mutex.unlock();
100
}
101
102
// 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.
103
void compile_pipeline(const Key &p_key, uint32_t p_key_hash, RS::PipelineSource p_source, bool p_high_priority) {
104
DEV_ASSERT((creation_object != nullptr) && (creation_function != nullptr) && "Creation object and function was not set before attempting to compile a pipeline.");
105
106
MutexLock local_lock(local_mutex);
107
if (compilation_set.has(p_key_hash)) {
108
// Check if the pipeline was already submitted.
109
return;
110
}
111
112
// Record the pipeline as submitted, a task can't be started for it again.
113
compilation_set.insert(p_key_hash);
114
115
if (compilations_mutex != nullptr) {
116
MutexLock compilations_lock(*compilations_mutex);
117
compilations[p_source]++;
118
}
119
120
#if PRINT_PIPELINE_COMPILATION_KEYS
121
String source_name = "UNKNOWN";
122
switch (p_source) {
123
case RS::PIPELINE_SOURCE_CANVAS:
124
source_name = "CANVAS";
125
break;
126
case RS::PIPELINE_SOURCE_MESH:
127
source_name = "MESH";
128
break;
129
case RS::PIPELINE_SOURCE_SURFACE:
130
source_name = "SURFACE";
131
break;
132
case RS::PIPELINE_SOURCE_DRAW:
133
source_name = "DRAW";
134
break;
135
case RS::PIPELINE_SOURCE_SPECIALIZATION:
136
source_name = "SPECIALIZATION";
137
break;
138
}
139
140
print_line("HASH:", p_key_hash, "SOURCE:", source_name);
141
#endif
142
143
// Queue a background compilation task.
144
WorkerThreadPool::TaskID task_id = WorkerThreadPool::get_singleton()->add_template_task(creation_object, creation_function, p_key, p_high_priority, "PipelineCompilation");
145
compilation_tasks.insert(p_key_hash, task_id);
146
}
147
148
void wait_for_pipeline(uint32_t p_key_hash) {
149
WorkerThreadPool::TaskID task_id_to_wait = WorkerThreadPool::INVALID_TASK_ID;
150
151
{
152
MutexLock local_lock(local_mutex);
153
if (!compilation_set.has(p_key_hash)) {
154
// The pipeline was never submitted, we can't wait for it.
155
return;
156
}
157
158
HashMap<uint32_t, WorkerThreadPool::TaskID>::Iterator task_it = compilation_tasks.find(p_key_hash);
159
if (task_it != compilation_tasks.end()) {
160
// Wait for and remove the compilation task if it exists.
161
task_id_to_wait = task_it->value;
162
compilation_tasks.remove(task_it);
163
}
164
}
165
166
if (task_id_to_wait != WorkerThreadPool::INVALID_TASK_ID) {
167
WorkerThreadPool::get_singleton()->wait_for_task_completion(task_id_to_wait);
168
}
169
}
170
171
// 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.
172
RID get_pipeline(const Key &p_key, uint32_t p_key_hash, bool p_wait_for_compilation, RS::PipelineSource p_source) {
173
RBMap<uint32_t, RID>::Element *e = hash_map.find(p_key_hash);
174
175
if (e == nullptr) {
176
// Check if there's any new pipelines that need to be added and try again. This method triggers a mutex lock.
177
if (_add_new_pipelines_to_map()) {
178
e = hash_map.find(p_key_hash);
179
}
180
}
181
182
if (e == nullptr) {
183
// Request compilation. The method will ignore the request if it's already being compiled.
184
compile_pipeline(p_key, p_key_hash, p_source, p_wait_for_compilation);
185
186
if (p_wait_for_compilation) {
187
wait_for_pipeline(p_key_hash);
188
_add_new_pipelines_to_map();
189
190
e = hash_map.find(p_key_hash);
191
if (e != nullptr) {
192
return e->value();
193
} else {
194
// Pipeline could not be compiled due to an internal error. Store an empty RID so compilation is not attempted again.
195
hash_map[p_key_hash] = RID();
196
return RID();
197
}
198
} else {
199
return RID();
200
}
201
} else {
202
return e->value();
203
}
204
}
205
206
// Delete all cached pipelines. Can stall if background compilation is in progress.
207
void clear_pipelines() {
208
_wait_for_all_pipelines();
209
_add_new_pipelines_to_map();
210
211
for (KeyValue<uint32_t, RID> entry : hash_map) {
212
RD::get_singleton()->free_rid(entry.value);
213
}
214
215
hash_map.clear();
216
compilation_set.clear();
217
}
218
219
// Set the external pipeline compilations array to increase the counters on every time a pipeline is compiled.
220
void set_compilations(uint32_t *p_compilations, Mutex *p_compilations_mutex) {
221
compilations = p_compilations;
222
compilations_mutex = p_compilations_mutex;
223
}
224
225
void set_creation_object_and_function(CreationClass *p_creation_object, CreationFunction p_creation_function) {
226
creation_object = p_creation_object;
227
creation_function = p_creation_function;
228
}
229
230
PipelineHashMapRD() {}
231
232
~PipelineHashMapRD() {
233
clear_pipelines();
234
}
235
};
236
237