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
10278 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 "servers/rendering/rendering_device.h"
34
#include "servers/rendering_server.h"
35
36
#define PRINT_PIPELINE_COMPILATION_KEYS 0
37
38
template <typename Key, typename CreationClass, typename CreationFunction>
39
class PipelineHashMapRD {
40
private:
41
CreationClass *creation_object = nullptr;
42
CreationFunction creation_function = nullptr;
43
Mutex *compilations_mutex = nullptr;
44
uint32_t *compilations = nullptr;
45
RBMap<uint32_t, RID> hash_map;
46
LocalVector<Pair<uint32_t, RID>> compiled_queue;
47
Mutex compiled_queue_mutex;
48
RBSet<uint32_t> compilation_set;
49
HashMap<uint32_t, WorkerThreadPool::TaskID> compilation_tasks;
50
Mutex local_mutex;
51
52
bool _add_new_pipelines_to_map() {
53
thread_local Vector<uint32_t> hashes_added;
54
hashes_added.clear();
55
56
{
57
MutexLock lock(compiled_queue_mutex);
58
for (const Pair<uint32_t, RID> &pair : compiled_queue) {
59
hash_map[pair.first] = pair.second;
60
hashes_added.push_back(pair.first);
61
}
62
63
compiled_queue.clear();
64
}
65
66
{
67
MutexLock local_lock(local_mutex);
68
for (uint32_t hash : hashes_added) {
69
HashMap<uint32_t, WorkerThreadPool::TaskID>::Iterator task_it = compilation_tasks.find(hash);
70
if (task_it != compilation_tasks.end()) {
71
compilation_tasks.remove(task_it);
72
}
73
}
74
}
75
76
return !hashes_added.is_empty();
77
}
78
79
void _wait_for_all_pipelines() {
80
thread_local LocalVector<WorkerThreadPool::TaskID> tasks_to_wait;
81
tasks_to_wait.clear();
82
{
83
MutexLock local_lock(local_mutex);
84
for (KeyValue<uint32_t, WorkerThreadPool::TaskID> key_value : compilation_tasks) {
85
tasks_to_wait.push_back(key_value.value);
86
}
87
}
88
89
for (WorkerThreadPool::TaskID task_id : tasks_to_wait) {
90
WorkerThreadPool::get_singleton()->wait_for_task_completion(task_id);
91
}
92
}
93
94
public:
95
void add_compiled_pipeline(uint32_t p_hash, RID p_pipeline) {
96
compiled_queue_mutex.lock();
97
compiled_queue.push_back({ p_hash, p_pipeline });
98
compiled_queue_mutex.unlock();
99
}
100
101
// 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.
102
void compile_pipeline(const Key &p_key, uint32_t p_key_hash, RS::PipelineSource p_source, bool p_high_priority) {
103
DEV_ASSERT((creation_object != nullptr) && (creation_function != nullptr) && "Creation object and function was not set before attempting to compile a pipeline.");
104
105
MutexLock local_lock(local_mutex);
106
if (compilation_set.has(p_key_hash)) {
107
// Check if the pipeline was already submitted.
108
return;
109
}
110
111
// Record the pipeline as submitted, a task can't be started for it again.
112
compilation_set.insert(p_key_hash);
113
114
if (compilations_mutex != nullptr) {
115
MutexLock compilations_lock(*compilations_mutex);
116
compilations[p_source]++;
117
}
118
119
#if PRINT_PIPELINE_COMPILATION_KEYS
120
String source_name = "UNKNOWN";
121
switch (p_source) {
122
case RS::PIPELINE_SOURCE_CANVAS:
123
source_name = "CANVAS";
124
break;
125
case RS::PIPELINE_SOURCE_MESH:
126
source_name = "MESH";
127
break;
128
case RS::PIPELINE_SOURCE_SURFACE:
129
source_name = "SURFACE";
130
break;
131
case RS::PIPELINE_SOURCE_DRAW:
132
source_name = "DRAW";
133
break;
134
case RS::PIPELINE_SOURCE_SPECIALIZATION:
135
source_name = "SPECIALIZATION";
136
break;
137
}
138
139
print_line("HASH:", p_key_hash, "SOURCE:", source_name);
140
#endif
141
142
// Queue a background compilation task.
143
WorkerThreadPool::TaskID task_id = WorkerThreadPool::get_singleton()->add_template_task(creation_object, creation_function, p_key, p_high_priority, "PipelineCompilation");
144
compilation_tasks.insert(p_key_hash, task_id);
145
}
146
147
void wait_for_pipeline(uint32_t p_key_hash) {
148
WorkerThreadPool::TaskID task_id_to_wait = WorkerThreadPool::INVALID_TASK_ID;
149
150
{
151
MutexLock local_lock(local_mutex);
152
if (!compilation_set.has(p_key_hash)) {
153
// The pipeline was never submitted, we can't wait for it.
154
return;
155
}
156
157
HashMap<uint32_t, WorkerThreadPool::TaskID>::Iterator task_it = compilation_tasks.find(p_key_hash);
158
if (task_it != compilation_tasks.end()) {
159
// Wait for and remove the compilation task if it exists.
160
task_id_to_wait = task_it->value;
161
compilation_tasks.remove(task_it);
162
}
163
}
164
165
if (task_id_to_wait != WorkerThreadPool::INVALID_TASK_ID) {
166
WorkerThreadPool::get_singleton()->wait_for_task_completion(task_id_to_wait);
167
}
168
}
169
170
// 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.
171
RID get_pipeline(const Key &p_key, uint32_t p_key_hash, bool p_wait_for_compilation, RS::PipelineSource p_source) {
172
RBMap<uint32_t, RID>::Element *e = hash_map.find(p_key_hash);
173
174
if (e == nullptr) {
175
// Check if there's any new pipelines that need to be added and try again. This method triggers a mutex lock.
176
if (_add_new_pipelines_to_map()) {
177
e = hash_map.find(p_key_hash);
178
}
179
}
180
181
if (e == nullptr) {
182
// Request compilation. The method will ignore the request if it's already being compiled.
183
compile_pipeline(p_key, p_key_hash, p_source, p_wait_for_compilation);
184
185
if (p_wait_for_compilation) {
186
wait_for_pipeline(p_key_hash);
187
_add_new_pipelines_to_map();
188
189
e = hash_map.find(p_key_hash);
190
if (e != nullptr) {
191
return e->value();
192
} else {
193
// Pipeline could not be compiled due to an internal error. Store an empty RID so compilation is not attempted again.
194
hash_map[p_key_hash] = RID();
195
return RID();
196
}
197
} else {
198
return RID();
199
}
200
} else {
201
return e->value();
202
}
203
}
204
205
// Delete all cached pipelines. Can stall if background compilation is in progress.
206
void clear_pipelines() {
207
_wait_for_all_pipelines();
208
_add_new_pipelines_to_map();
209
210
for (KeyValue<uint32_t, RID> entry : hash_map) {
211
RD::get_singleton()->free(entry.value);
212
}
213
214
hash_map.clear();
215
compilation_set.clear();
216
}
217
218
// Set the external pipeline compilations array to increase the counters on every time a pipeline is compiled.
219
void set_compilations(uint32_t *p_compilations, Mutex *p_compilations_mutex) {
220
compilations = p_compilations;
221
compilations_mutex = p_compilations_mutex;
222
}
223
224
void set_creation_object_and_function(CreationClass *p_creation_object, CreationFunction p_creation_function) {
225
creation_object = p_creation_object;
226
creation_function = p_creation_function;
227
}
228
229
PipelineHashMapRD() {}
230
231
~PipelineHashMapRD() {
232
clear_pipelines();
233
}
234
};
235
236