1 | /**************************************************************************/ |
2 | /* shader_rd.cpp */ |
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 | #include "shader_rd.h" |
32 | |
33 | #include "core/io/compression.h" |
34 | #include "core/io/dir_access.h" |
35 | #include "core/io/file_access.h" |
36 | #include "core/object/worker_thread_pool.h" |
37 | #include "core/version.h" |
38 | #include "renderer_compositor_rd.h" |
39 | #include "servers/rendering/rendering_device.h" |
40 | #include "thirdparty/misc/smolv.h" |
41 | |
42 | void ShaderRD::_add_stage(const char *p_code, StageType p_stage_type) { |
43 | Vector<String> lines = String(p_code).split("\n" ); |
44 | |
45 | String text; |
46 | |
47 | for (int i = 0; i < lines.size(); i++) { |
48 | String l = lines[i]; |
49 | bool push_chunk = false; |
50 | |
51 | StageTemplate::Chunk chunk; |
52 | |
53 | if (l.begins_with("#VERSION_DEFINES" )) { |
54 | chunk.type = StageTemplate::Chunk::TYPE_VERSION_DEFINES; |
55 | push_chunk = true; |
56 | } else if (l.begins_with("#GLOBALS" )) { |
57 | switch (p_stage_type) { |
58 | case STAGE_TYPE_VERTEX: |
59 | chunk.type = StageTemplate::Chunk::TYPE_VERTEX_GLOBALS; |
60 | break; |
61 | case STAGE_TYPE_FRAGMENT: |
62 | chunk.type = StageTemplate::Chunk::TYPE_FRAGMENT_GLOBALS; |
63 | break; |
64 | case STAGE_TYPE_COMPUTE: |
65 | chunk.type = StageTemplate::Chunk::TYPE_COMPUTE_GLOBALS; |
66 | break; |
67 | default: { |
68 | } |
69 | } |
70 | |
71 | push_chunk = true; |
72 | } else if (l.begins_with("#MATERIAL_UNIFORMS" )) { |
73 | chunk.type = StageTemplate::Chunk::TYPE_MATERIAL_UNIFORMS; |
74 | push_chunk = true; |
75 | } else if (l.begins_with("#CODE" )) { |
76 | chunk.type = StageTemplate::Chunk::TYPE_CODE; |
77 | push_chunk = true; |
78 | chunk.code = l.replace_first("#CODE" , String()).replace(":" , "" ).strip_edges().to_upper(); |
79 | } else { |
80 | text += l + "\n" ; |
81 | } |
82 | |
83 | if (push_chunk) { |
84 | if (!text.is_empty()) { |
85 | StageTemplate::Chunk text_chunk; |
86 | text_chunk.type = StageTemplate::Chunk::TYPE_TEXT; |
87 | text_chunk.text = text.utf8(); |
88 | stage_templates[p_stage_type].chunks.push_back(text_chunk); |
89 | text = String(); |
90 | } |
91 | stage_templates[p_stage_type].chunks.push_back(chunk); |
92 | } |
93 | } |
94 | |
95 | if (!text.is_empty()) { |
96 | StageTemplate::Chunk text_chunk; |
97 | text_chunk.type = StageTemplate::Chunk::TYPE_TEXT; |
98 | text_chunk.text = text.utf8(); |
99 | stage_templates[p_stage_type].chunks.push_back(text_chunk); |
100 | text = String(); |
101 | } |
102 | } |
103 | |
104 | void ShaderRD::setup(const char *p_vertex_code, const char *p_fragment_code, const char *p_compute_code, const char *p_name) { |
105 | name = p_name; |
106 | |
107 | if (p_compute_code) { |
108 | _add_stage(p_compute_code, STAGE_TYPE_COMPUTE); |
109 | is_compute = true; |
110 | } else { |
111 | is_compute = false; |
112 | if (p_vertex_code) { |
113 | _add_stage(p_vertex_code, STAGE_TYPE_VERTEX); |
114 | } |
115 | if (p_fragment_code) { |
116 | _add_stage(p_fragment_code, STAGE_TYPE_FRAGMENT); |
117 | } |
118 | } |
119 | |
120 | StringBuilder tohash; |
121 | tohash.append("[GodotVersionNumber]" ); |
122 | tohash.append(VERSION_NUMBER); |
123 | tohash.append("[GodotVersionHash]" ); |
124 | tohash.append(VERSION_HASH); |
125 | tohash.append("[SpirvCacheKey]" ); |
126 | tohash.append(RenderingDevice::get_singleton()->shader_get_spirv_cache_key()); |
127 | tohash.append("[BinaryCacheKey]" ); |
128 | tohash.append(RenderingDevice::get_singleton()->shader_get_binary_cache_key()); |
129 | tohash.append("[Vertex]" ); |
130 | tohash.append(p_vertex_code ? p_vertex_code : "" ); |
131 | tohash.append("[Fragment]" ); |
132 | tohash.append(p_fragment_code ? p_fragment_code : "" ); |
133 | tohash.append("[Compute]" ); |
134 | tohash.append(p_compute_code ? p_compute_code : "" ); |
135 | |
136 | base_sha256 = tohash.as_string().sha256_text(); |
137 | } |
138 | |
139 | RID ShaderRD::version_create() { |
140 | //initialize() was never called |
141 | ERR_FAIL_COND_V(group_to_variant_map.size() == 0, RID()); |
142 | |
143 | Version version; |
144 | version.dirty = true; |
145 | version.valid = false; |
146 | version.initialize_needed = true; |
147 | version.variants = nullptr; |
148 | return version_owner.make_rid(version); |
149 | } |
150 | |
151 | void ShaderRD::_initialize_version(Version *p_version) { |
152 | _clear_version(p_version); |
153 | |
154 | p_version->valid = false; |
155 | p_version->dirty = false; |
156 | |
157 | p_version->variants = memnew_arr(RID, variant_defines.size()); |
158 | } |
159 | |
160 | void ShaderRD::_clear_version(Version *p_version) { |
161 | // Clear versions if they exist. |
162 | if (p_version->variants) { |
163 | for (int i = 0; i < variant_defines.size(); i++) { |
164 | if (p_version->variants[i].is_valid()) { |
165 | RD::get_singleton()->free(p_version->variants[i]); |
166 | } |
167 | } |
168 | |
169 | memdelete_arr(p_version->variants); |
170 | if (p_version->variant_data) { |
171 | memdelete_arr(p_version->variant_data); |
172 | } |
173 | p_version->variants = nullptr; |
174 | } |
175 | } |
176 | |
177 | void ShaderRD::_build_variant_code(StringBuilder &builder, uint32_t p_variant, const Version *p_version, const StageTemplate &p_template) { |
178 | for (const StageTemplate::Chunk &chunk : p_template.chunks) { |
179 | switch (chunk.type) { |
180 | case StageTemplate::Chunk::TYPE_VERSION_DEFINES: { |
181 | builder.append("\n" ); //make sure defines begin at newline |
182 | builder.append(general_defines.get_data()); |
183 | builder.append(variant_defines[p_variant].text.get_data()); |
184 | for (int j = 0; j < p_version->custom_defines.size(); j++) { |
185 | builder.append(p_version->custom_defines[j].get_data()); |
186 | } |
187 | builder.append("\n" ); //make sure defines begin at newline |
188 | if (p_version->uniforms.size()) { |
189 | builder.append("#define MATERIAL_UNIFORMS_USED\n" ); |
190 | } |
191 | for (const KeyValue<StringName, CharString> &E : p_version->code_sections) { |
192 | builder.append(String("#define " ) + String(E.key) + "_CODE_USED\n" ); |
193 | } |
194 | #if defined(MACOS_ENABLED) || defined(IOS_ENABLED) |
195 | builder.append("#define MOLTENVK_USED\n" ); |
196 | #endif |
197 | builder.append(String("#define RENDER_DRIVER_" ) + OS::get_singleton()->get_current_rendering_driver_name().to_upper() + "\n" ); |
198 | } break; |
199 | case StageTemplate::Chunk::TYPE_MATERIAL_UNIFORMS: { |
200 | builder.append(p_version->uniforms.get_data()); //uniforms (same for vertex and fragment) |
201 | } break; |
202 | case StageTemplate::Chunk::TYPE_VERTEX_GLOBALS: { |
203 | builder.append(p_version->vertex_globals.get_data()); // vertex globals |
204 | } break; |
205 | case StageTemplate::Chunk::TYPE_FRAGMENT_GLOBALS: { |
206 | builder.append(p_version->fragment_globals.get_data()); // fragment globals |
207 | } break; |
208 | case StageTemplate::Chunk::TYPE_COMPUTE_GLOBALS: { |
209 | builder.append(p_version->compute_globals.get_data()); // compute globals |
210 | } break; |
211 | case StageTemplate::Chunk::TYPE_CODE: { |
212 | if (p_version->code_sections.has(chunk.code)) { |
213 | builder.append(p_version->code_sections[chunk.code].get_data()); |
214 | } |
215 | } break; |
216 | case StageTemplate::Chunk::TYPE_TEXT: { |
217 | builder.append(chunk.text.get_data()); |
218 | } break; |
219 | } |
220 | } |
221 | } |
222 | |
223 | void ShaderRD::_compile_variant(uint32_t p_variant, const CompileData *p_data) { |
224 | uint32_t variant = group_to_variant_map[p_data->group][p_variant]; |
225 | |
226 | if (!variants_enabled[variant]) { |
227 | return; // Variant is disabled, return. |
228 | } |
229 | |
230 | Vector<RD::ShaderStageSPIRVData> stages; |
231 | |
232 | String error; |
233 | String current_source; |
234 | RD::ShaderStage current_stage = RD::SHADER_STAGE_VERTEX; |
235 | bool build_ok = true; |
236 | |
237 | if (!is_compute) { |
238 | //vertex stage |
239 | |
240 | StringBuilder builder; |
241 | _build_variant_code(builder, variant, p_data->version, stage_templates[STAGE_TYPE_VERTEX]); |
242 | |
243 | current_source = builder.as_string(); |
244 | RD::ShaderStageSPIRVData stage; |
245 | stage.spir_v = RD::get_singleton()->shader_compile_spirv_from_source(RD::SHADER_STAGE_VERTEX, current_source, RD::SHADER_LANGUAGE_GLSL, &error); |
246 | if (stage.spir_v.size() == 0) { |
247 | build_ok = false; |
248 | } else { |
249 | stage.shader_stage = RD::SHADER_STAGE_VERTEX; |
250 | stages.push_back(stage); |
251 | } |
252 | } |
253 | |
254 | if (!is_compute && build_ok) { |
255 | //fragment stage |
256 | current_stage = RD::SHADER_STAGE_FRAGMENT; |
257 | |
258 | StringBuilder builder; |
259 | _build_variant_code(builder, variant, p_data->version, stage_templates[STAGE_TYPE_FRAGMENT]); |
260 | |
261 | current_source = builder.as_string(); |
262 | RD::ShaderStageSPIRVData stage; |
263 | stage.spir_v = RD::get_singleton()->shader_compile_spirv_from_source(RD::SHADER_STAGE_FRAGMENT, current_source, RD::SHADER_LANGUAGE_GLSL, &error); |
264 | if (stage.spir_v.size() == 0) { |
265 | build_ok = false; |
266 | } else { |
267 | stage.shader_stage = RD::SHADER_STAGE_FRAGMENT; |
268 | stages.push_back(stage); |
269 | } |
270 | } |
271 | |
272 | if (is_compute) { |
273 | //compute stage |
274 | current_stage = RD::SHADER_STAGE_COMPUTE; |
275 | |
276 | StringBuilder builder; |
277 | _build_variant_code(builder, variant, p_data->version, stage_templates[STAGE_TYPE_COMPUTE]); |
278 | |
279 | current_source = builder.as_string(); |
280 | |
281 | RD::ShaderStageSPIRVData stage; |
282 | stage.spir_v = RD::get_singleton()->shader_compile_spirv_from_source(RD::SHADER_STAGE_COMPUTE, current_source, RD::SHADER_LANGUAGE_GLSL, &error); |
283 | if (stage.spir_v.size() == 0) { |
284 | build_ok = false; |
285 | } else { |
286 | stage.shader_stage = RD::SHADER_STAGE_COMPUTE; |
287 | stages.push_back(stage); |
288 | } |
289 | } |
290 | |
291 | if (!build_ok) { |
292 | MutexLock lock(variant_set_mutex); //properly print the errors |
293 | ERR_PRINT("Error compiling " + String(current_stage == RD::SHADER_STAGE_COMPUTE ? "Compute " : (current_stage == RD::SHADER_STAGE_VERTEX ? "Vertex" : "Fragment" )) + " shader, variant #" + itos(variant) + " (" + variant_defines[variant].text.get_data() + ")." ); |
294 | ERR_PRINT(error); |
295 | |
296 | #ifdef DEBUG_ENABLED |
297 | ERR_PRINT("code:\n" + current_source.get_with_code_lines()); |
298 | #endif |
299 | return; |
300 | } |
301 | |
302 | Vector<uint8_t> shader_data = RD::get_singleton()->shader_compile_binary_from_spirv(stages, name + ":" + itos(variant)); |
303 | |
304 | ERR_FAIL_COND(shader_data.size() == 0); |
305 | |
306 | { |
307 | MutexLock lock(variant_set_mutex); |
308 | |
309 | p_data->version->variants[variant] = RD::get_singleton()->shader_create_from_bytecode(shader_data, p_data->version->variants[variant]); |
310 | p_data->version->variant_data[variant] = shader_data; |
311 | } |
312 | } |
313 | |
314 | RS::ShaderNativeSourceCode ShaderRD::version_get_native_source_code(RID p_version) { |
315 | Version *version = version_owner.get_or_null(p_version); |
316 | RS::ShaderNativeSourceCode source_code; |
317 | ERR_FAIL_COND_V(!version, source_code); |
318 | |
319 | source_code.versions.resize(variant_defines.size()); |
320 | |
321 | for (int i = 0; i < source_code.versions.size(); i++) { |
322 | if (!is_compute) { |
323 | //vertex stage |
324 | |
325 | StringBuilder builder; |
326 | _build_variant_code(builder, i, version, stage_templates[STAGE_TYPE_VERTEX]); |
327 | |
328 | RS::ShaderNativeSourceCode::Version::Stage stage; |
329 | stage.name = "vertex" ; |
330 | stage.code = builder.as_string(); |
331 | |
332 | source_code.versions.write[i].stages.push_back(stage); |
333 | } |
334 | |
335 | if (!is_compute) { |
336 | //fragment stage |
337 | |
338 | StringBuilder builder; |
339 | _build_variant_code(builder, i, version, stage_templates[STAGE_TYPE_FRAGMENT]); |
340 | |
341 | RS::ShaderNativeSourceCode::Version::Stage stage; |
342 | stage.name = "fragment" ; |
343 | stage.code = builder.as_string(); |
344 | |
345 | source_code.versions.write[i].stages.push_back(stage); |
346 | } |
347 | |
348 | if (is_compute) { |
349 | //compute stage |
350 | |
351 | StringBuilder builder; |
352 | _build_variant_code(builder, i, version, stage_templates[STAGE_TYPE_COMPUTE]); |
353 | |
354 | RS::ShaderNativeSourceCode::Version::Stage stage; |
355 | stage.name = "compute" ; |
356 | stage.code = builder.as_string(); |
357 | |
358 | source_code.versions.write[i].stages.push_back(stage); |
359 | } |
360 | } |
361 | |
362 | return source_code; |
363 | } |
364 | |
365 | String ShaderRD::_version_get_sha1(Version *p_version) const { |
366 | StringBuilder hash_build; |
367 | |
368 | hash_build.append("[uniforms]" ); |
369 | hash_build.append(p_version->uniforms.get_data()); |
370 | hash_build.append("[vertex_globals]" ); |
371 | hash_build.append(p_version->vertex_globals.get_data()); |
372 | hash_build.append("[fragment_globals]" ); |
373 | hash_build.append(p_version->fragment_globals.get_data()); |
374 | hash_build.append("[compute_globals]" ); |
375 | hash_build.append(p_version->compute_globals.get_data()); |
376 | |
377 | Vector<StringName> code_sections; |
378 | for (const KeyValue<StringName, CharString> &E : p_version->code_sections) { |
379 | code_sections.push_back(E.key); |
380 | } |
381 | code_sections.sort_custom<StringName::AlphCompare>(); |
382 | |
383 | for (int i = 0; i < code_sections.size(); i++) { |
384 | hash_build.append(String("[code:" ) + String(code_sections[i]) + "]" ); |
385 | hash_build.append(p_version->code_sections[code_sections[i]].get_data()); |
386 | } |
387 | for (int i = 0; i < p_version->custom_defines.size(); i++) { |
388 | hash_build.append("[custom_defines:" + itos(i) + "]" ); |
389 | hash_build.append(p_version->custom_defines[i].get_data()); |
390 | } |
391 | |
392 | return hash_build.as_string().sha1_text(); |
393 | } |
394 | |
395 | static const char * = "GDSC" ; |
396 | static const uint32_t cache_file_version = 3; |
397 | |
398 | bool ShaderRD::_load_from_cache(Version *p_version, int p_group) { |
399 | String sha1 = _version_get_sha1(p_version); |
400 | String path = shader_cache_dir.path_join(name).path_join(group_sha256[p_group]).path_join(sha1) + ".cache" ; |
401 | |
402 | Ref<FileAccess> f = FileAccess::open(path, FileAccess::READ); |
403 | if (f.is_null()) { |
404 | return false; |
405 | } |
406 | |
407 | char [5] = { 0, 0, 0, 0, 0 }; |
408 | f->get_buffer((uint8_t *)header, 4); |
409 | ERR_FAIL_COND_V(header != String(shader_file_header), false); |
410 | |
411 | uint32_t file_version = f->get_32(); |
412 | if (file_version != cache_file_version) { |
413 | return false; // wrong version |
414 | } |
415 | |
416 | uint32_t variant_count = f->get_32(); |
417 | |
418 | ERR_FAIL_COND_V(variant_count != (uint32_t)group_to_variant_map[p_group].size(), false); //should not happen but check |
419 | |
420 | for (uint32_t i = 0; i < variant_count; i++) { |
421 | int variant_id = group_to_variant_map[p_group][i]; |
422 | uint32_t variant_size = f->get_32(); |
423 | ERR_FAIL_COND_V(variant_size == 0 && variants_enabled[variant_id], false); |
424 | if (!variants_enabled[variant_id]) { |
425 | continue; |
426 | } |
427 | Vector<uint8_t> variant_bytes; |
428 | variant_bytes.resize(variant_size); |
429 | |
430 | uint32_t br = f->get_buffer(variant_bytes.ptrw(), variant_size); |
431 | |
432 | ERR_FAIL_COND_V(br != variant_size, false); |
433 | |
434 | p_version->variant_data[variant_id] = variant_bytes; |
435 | } |
436 | |
437 | for (uint32_t i = 0; i < variant_count; i++) { |
438 | int variant_id = group_to_variant_map[p_group][i]; |
439 | if (!variants_enabled[variant_id]) { |
440 | MutexLock lock(variant_set_mutex); |
441 | p_version->variants[variant_id] = RID(); |
442 | continue; |
443 | } |
444 | { |
445 | MutexLock lock(variant_set_mutex); |
446 | RID shader = RD::get_singleton()->shader_create_from_bytecode(p_version->variant_data[variant_id], p_version->variants[variant_id]); |
447 | if (shader.is_null()) { |
448 | for (uint32_t j = 0; j < i; j++) { |
449 | int variant_free_id = group_to_variant_map[p_group][j]; |
450 | RD::get_singleton()->free(p_version->variants[variant_free_id]); |
451 | } |
452 | ERR_FAIL_COND_V(shader.is_null(), false); |
453 | } |
454 | |
455 | p_version->variants[variant_id] = shader; |
456 | } |
457 | } |
458 | |
459 | memdelete_arr(p_version->variant_data); //clear stages |
460 | p_version->variant_data = nullptr; |
461 | p_version->valid = true; |
462 | return true; |
463 | } |
464 | |
465 | void ShaderRD::_save_to_cache(Version *p_version, int p_group) { |
466 | String sha1 = _version_get_sha1(p_version); |
467 | String path = shader_cache_dir.path_join(name).path_join(group_sha256[p_group]).path_join(sha1) + ".cache" ; |
468 | |
469 | Ref<FileAccess> f = FileAccess::open(path, FileAccess::WRITE); |
470 | ERR_FAIL_COND(f.is_null()); |
471 | f->store_buffer((const uint8_t *)shader_file_header, 4); |
472 | f->store_32(cache_file_version); // File version. |
473 | uint32_t variant_count = group_to_variant_map[p_group].size(); |
474 | f->store_32(variant_count); // Variant count. |
475 | for (uint32_t i = 0; i < variant_count; i++) { |
476 | int variant_id = group_to_variant_map[p_group][i]; |
477 | f->store_32(p_version->variant_data[variant_id].size()); // Stage count. |
478 | f->store_buffer(p_version->variant_data[variant_id].ptr(), p_version->variant_data[variant_id].size()); |
479 | } |
480 | } |
481 | |
482 | void ShaderRD::_allocate_placeholders(Version *p_version, int p_group) { |
483 | for (uint32_t i = 0; i < group_to_variant_map[p_group].size(); i++) { |
484 | int variant_id = group_to_variant_map[p_group][i]; |
485 | RID shader = RD::get_singleton()->shader_create_placeholder(); |
486 | { |
487 | MutexLock lock(variant_set_mutex); |
488 | p_version->variants[variant_id] = shader; |
489 | } |
490 | } |
491 | } |
492 | |
493 | // Try to compile all variants for a given group. |
494 | // Will skip variants that are disabled. |
495 | void ShaderRD::_compile_version(Version *p_version, int p_group) { |
496 | if (!group_enabled[p_group]) { |
497 | return; |
498 | } |
499 | |
500 | typedef Vector<uint8_t> ShaderStageData; |
501 | p_version->variant_data = memnew_arr(ShaderStageData, variant_defines.size()); |
502 | |
503 | p_version->dirty = false; |
504 | |
505 | if (shader_cache_dir_valid) { |
506 | if (_load_from_cache(p_version, p_group)) { |
507 | return; |
508 | } |
509 | } |
510 | |
511 | CompileData compile_data; |
512 | compile_data.version = p_version; |
513 | compile_data.group = p_group; |
514 | |
515 | #if 1 |
516 | WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &ShaderRD::_compile_variant, &compile_data, group_to_variant_map[p_group].size(), -1, true, SNAME("ShaderCompilation" )); |
517 | WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task); |
518 | |
519 | #else |
520 | for (uint32_t i = 0; i < group_to_variant_map[p_group].size(); i++) { |
521 | _compile_variant(i, &compile_data); |
522 | } |
523 | #endif |
524 | |
525 | bool all_valid = true; |
526 | |
527 | for (uint32_t i = 0; i < group_to_variant_map[p_group].size(); i++) { |
528 | int variant_id = group_to_variant_map[p_group][i]; |
529 | if (!variants_enabled[variant_id]) { |
530 | continue; // Disabled. |
531 | } |
532 | if (p_version->variants[variant_id].is_null()) { |
533 | all_valid = false; |
534 | break; |
535 | } |
536 | } |
537 | |
538 | if (!all_valid) { |
539 | // Clear versions if they exist. |
540 | for (int i = 0; i < variant_defines.size(); i++) { |
541 | if (!variants_enabled[i] || !group_enabled[variant_defines[i].group]) { |
542 | continue; // Disabled. |
543 | } |
544 | if (!p_version->variants[i].is_null()) { |
545 | RD::get_singleton()->free(p_version->variants[i]); |
546 | } |
547 | } |
548 | memdelete_arr(p_version->variants); |
549 | if (p_version->variant_data) { |
550 | memdelete_arr(p_version->variant_data); |
551 | } |
552 | p_version->variants = nullptr; |
553 | p_version->variant_data = nullptr; |
554 | return; |
555 | } else if (shader_cache_dir_valid) { |
556 | // Save shader cache. |
557 | _save_to_cache(p_version, p_group); |
558 | } |
559 | |
560 | memdelete_arr(p_version->variant_data); //clear stages |
561 | p_version->variant_data = nullptr; |
562 | |
563 | p_version->valid = true; |
564 | } |
565 | |
566 | void ShaderRD::version_set_code(RID p_version, const HashMap<String, String> &p_code, const String &p_uniforms, const String &p_vertex_globals, const String &p_fragment_globals, const Vector<String> &p_custom_defines) { |
567 | ERR_FAIL_COND(is_compute); |
568 | |
569 | Version *version = version_owner.get_or_null(p_version); |
570 | ERR_FAIL_COND(!version); |
571 | version->vertex_globals = p_vertex_globals.utf8(); |
572 | version->fragment_globals = p_fragment_globals.utf8(); |
573 | version->uniforms = p_uniforms.utf8(); |
574 | version->code_sections.clear(); |
575 | for (const KeyValue<String, String> &E : p_code) { |
576 | version->code_sections[StringName(E.key.to_upper())] = E.value.utf8(); |
577 | } |
578 | |
579 | version->custom_defines.clear(); |
580 | for (int i = 0; i < p_custom_defines.size(); i++) { |
581 | version->custom_defines.push_back(p_custom_defines[i].utf8()); |
582 | } |
583 | |
584 | version->dirty = true; |
585 | if (version->initialize_needed) { |
586 | _initialize_version(version); |
587 | for (int i = 0; i < group_enabled.size(); i++) { |
588 | if (!group_enabled[i]) { |
589 | _allocate_placeholders(version, i); |
590 | continue; |
591 | } |
592 | _compile_version(version, i); |
593 | } |
594 | version->initialize_needed = false; |
595 | } |
596 | } |
597 | |
598 | void ShaderRD::version_set_compute_code(RID p_version, const HashMap<String, String> &p_code, const String &p_uniforms, const String &p_compute_globals, const Vector<String> &p_custom_defines) { |
599 | ERR_FAIL_COND(!is_compute); |
600 | |
601 | Version *version = version_owner.get_or_null(p_version); |
602 | ERR_FAIL_COND(!version); |
603 | |
604 | version->compute_globals = p_compute_globals.utf8(); |
605 | version->uniforms = p_uniforms.utf8(); |
606 | |
607 | version->code_sections.clear(); |
608 | for (const KeyValue<String, String> &E : p_code) { |
609 | version->code_sections[StringName(E.key.to_upper())] = E.value.utf8(); |
610 | } |
611 | |
612 | version->custom_defines.clear(); |
613 | for (int i = 0; i < p_custom_defines.size(); i++) { |
614 | version->custom_defines.push_back(p_custom_defines[i].utf8()); |
615 | } |
616 | |
617 | version->dirty = true; |
618 | if (version->initialize_needed) { |
619 | _initialize_version(version); |
620 | for (int i = 0; i < group_enabled.size(); i++) { |
621 | if (!group_enabled[i]) { |
622 | _allocate_placeholders(version, i); |
623 | continue; |
624 | } |
625 | _compile_version(version, i); |
626 | } |
627 | version->initialize_needed = false; |
628 | } |
629 | } |
630 | |
631 | bool ShaderRD::version_is_valid(RID p_version) { |
632 | Version *version = version_owner.get_or_null(p_version); |
633 | ERR_FAIL_COND_V(!version, false); |
634 | |
635 | if (version->dirty) { |
636 | _initialize_version(version); |
637 | for (int i = 0; i < group_enabled.size(); i++) { |
638 | if (!group_enabled[i]) { |
639 | _allocate_placeholders(version, i); |
640 | continue; |
641 | } |
642 | _compile_version(version, i); |
643 | } |
644 | } |
645 | |
646 | return version->valid; |
647 | } |
648 | |
649 | bool ShaderRD::version_free(RID p_version) { |
650 | if (version_owner.owns(p_version)) { |
651 | Version *version = version_owner.get_or_null(p_version); |
652 | _clear_version(version); |
653 | version_owner.free(p_version); |
654 | } else { |
655 | return false; |
656 | } |
657 | |
658 | return true; |
659 | } |
660 | |
661 | void ShaderRD::set_variant_enabled(int p_variant, bool p_enabled) { |
662 | ERR_FAIL_COND(version_owner.get_rid_count() > 0); //versions exist |
663 | ERR_FAIL_INDEX(p_variant, variants_enabled.size()); |
664 | variants_enabled.write[p_variant] = p_enabled; |
665 | } |
666 | |
667 | bool ShaderRD::is_variant_enabled(int p_variant) const { |
668 | ERR_FAIL_INDEX_V(p_variant, variants_enabled.size(), false); |
669 | return variants_enabled[p_variant]; |
670 | } |
671 | |
672 | void ShaderRD::enable_group(int p_group) { |
673 | ERR_FAIL_INDEX(p_group, group_enabled.size()); |
674 | |
675 | if (group_enabled[p_group]) { |
676 | // Group already enabled, do nothing. |
677 | return; |
678 | } |
679 | |
680 | group_enabled.write[p_group] = true; |
681 | |
682 | // Compile all versions again to include the new group. |
683 | List<RID> all_versions; |
684 | version_owner.get_owned_list(&all_versions); |
685 | for (int i = 0; i < all_versions.size(); i++) { |
686 | Version *version = version_owner.get_or_null(all_versions[i]); |
687 | _compile_version(version, p_group); |
688 | } |
689 | } |
690 | |
691 | bool ShaderRD::is_group_enabled(int p_group) const { |
692 | return group_enabled[p_group]; |
693 | } |
694 | |
695 | bool ShaderRD::shader_cache_cleanup_on_start = false; |
696 | |
697 | ShaderRD::ShaderRD() { |
698 | // Do not feel forced to use this, in most cases it makes little to no difference. |
699 | bool use_32_threads = false; |
700 | if (RD::get_singleton()->get_device_vendor_name() == "NVIDIA" ) { |
701 | use_32_threads = true; |
702 | } |
703 | String base_compute_define_text; |
704 | if (use_32_threads) { |
705 | base_compute_define_text = "\n#define NATIVE_LOCAL_GROUP_SIZE 32\n#define NATIVE_LOCAL_SIZE_2D_X 8\n#define NATIVE_LOCAL_SIZE_2D_Y 4\n" ; |
706 | } else { |
707 | base_compute_define_text = "\n#define NATIVE_LOCAL_GROUP_SIZE 64\n#define NATIVE_LOCAL_SIZE_2D_X 8\n#define NATIVE_LOCAL_SIZE_2D_Y 8\n" ; |
708 | } |
709 | |
710 | base_compute_defines = base_compute_define_text.ascii(); |
711 | } |
712 | |
713 | void ShaderRD::initialize(const Vector<String> &p_variant_defines, const String &p_general_defines) { |
714 | ERR_FAIL_COND(variant_defines.size()); |
715 | ERR_FAIL_COND(p_variant_defines.size() == 0); |
716 | |
717 | general_defines = p_general_defines.utf8(); |
718 | |
719 | // When initialized this way, there is just one group and its always enabled. |
720 | group_to_variant_map.insert(0, LocalVector<int>{}); |
721 | group_enabled.push_back(true); |
722 | |
723 | for (int i = 0; i < p_variant_defines.size(); i++) { |
724 | variant_defines.push_back(VariantDefine(0, p_variant_defines[i], true)); |
725 | variants_enabled.push_back(true); |
726 | group_to_variant_map[0].push_back(i); |
727 | } |
728 | |
729 | if (!shader_cache_dir.is_empty()) { |
730 | group_sha256.resize(1); |
731 | _initialize_cache(); |
732 | } |
733 | } |
734 | |
735 | void ShaderRD::_initialize_cache() { |
736 | for (const KeyValue<int, LocalVector<int>> &E : group_to_variant_map) { |
737 | StringBuilder hash_build; |
738 | |
739 | hash_build.append("[base_hash]" ); |
740 | hash_build.append(base_sha256); |
741 | hash_build.append("[general_defines]" ); |
742 | hash_build.append(general_defines.get_data()); |
743 | hash_build.append("[group_id]" ); |
744 | hash_build.append(itos(E.key)); |
745 | for (uint32_t i = 0; i < E.value.size(); i++) { |
746 | hash_build.append("[variant_defines:" + itos(E.value[i]) + "]" ); |
747 | hash_build.append(variant_defines[E.value[i]].text.get_data()); |
748 | } |
749 | |
750 | group_sha256[E.key] = hash_build.as_string().sha256_text(); |
751 | |
752 | Ref<DirAccess> d = DirAccess::open(shader_cache_dir); |
753 | ERR_FAIL_COND(d.is_null()); |
754 | if (d->change_dir(name) != OK) { |
755 | Error err = d->make_dir(name); |
756 | ERR_FAIL_COND(err != OK); |
757 | d->change_dir(name); |
758 | } |
759 | |
760 | // Erase other versions? |
761 | if (shader_cache_cleanup_on_start) { |
762 | } |
763 | // |
764 | if (d->change_dir(group_sha256[E.key]) != OK) { |
765 | Error err = d->make_dir(group_sha256[E.key]); |
766 | ERR_FAIL_COND(err != OK); |
767 | } |
768 | shader_cache_dir_valid = true; |
769 | |
770 | print_verbose("Shader '" + name + "' (group " + itos(E.key) + ") SHA256: " + group_sha256[E.key]); |
771 | } |
772 | } |
773 | |
774 | // Same as above, but allows specifying shader compilation groups. |
775 | void ShaderRD::initialize(const Vector<VariantDefine> &p_variant_defines, const String &p_general_defines) { |
776 | ERR_FAIL_COND(variant_defines.size()); |
777 | ERR_FAIL_COND(p_variant_defines.size() == 0); |
778 | |
779 | general_defines = p_general_defines.utf8(); |
780 | |
781 | int max_group_id = 0; |
782 | |
783 | for (int i = 0; i < p_variant_defines.size(); i++) { |
784 | // Fill variant array. |
785 | variant_defines.push_back(p_variant_defines[i]); |
786 | variants_enabled.push_back(true); |
787 | |
788 | // Map variant array index to group id, so we can iterate over groups later. |
789 | if (!group_to_variant_map.has(p_variant_defines[i].group)) { |
790 | group_to_variant_map.insert(p_variant_defines[i].group, LocalVector<int>{}); |
791 | } |
792 | group_to_variant_map[p_variant_defines[i].group].push_back(i); |
793 | |
794 | // Track max size. |
795 | if (p_variant_defines[i].group > max_group_id) { |
796 | max_group_id = p_variant_defines[i].group; |
797 | } |
798 | } |
799 | |
800 | // Set all to groups to false, then enable those that should be default. |
801 | group_enabled.resize_zeroed(max_group_id + 1); |
802 | bool *enabled_ptr = group_enabled.ptrw(); |
803 | for (int i = 0; i < p_variant_defines.size(); i++) { |
804 | if (p_variant_defines[i].default_enabled) { |
805 | enabled_ptr[p_variant_defines[i].group] = true; |
806 | } |
807 | } |
808 | |
809 | if (!shader_cache_dir.is_empty()) { |
810 | group_sha256.resize(max_group_id + 1); |
811 | _initialize_cache(); |
812 | } |
813 | } |
814 | |
815 | void ShaderRD::set_shader_cache_dir(const String &p_dir) { |
816 | shader_cache_dir = p_dir; |
817 | } |
818 | |
819 | void ShaderRD::set_shader_cache_save_compressed(bool p_enable) { |
820 | shader_cache_save_compressed = p_enable; |
821 | } |
822 | |
823 | void ShaderRD::set_shader_cache_save_compressed_zstd(bool p_enable) { |
824 | shader_cache_save_compressed_zstd = p_enable; |
825 | } |
826 | |
827 | void ShaderRD::set_shader_cache_save_debug(bool p_enable) { |
828 | shader_cache_save_debug = p_enable; |
829 | } |
830 | |
831 | String ShaderRD::shader_cache_dir; |
832 | bool ShaderRD::shader_cache_save_compressed = true; |
833 | bool ShaderRD::shader_cache_save_compressed_zstd = true; |
834 | bool ShaderRD::shader_cache_save_debug = true; |
835 | |
836 | ShaderRD::~ShaderRD() { |
837 | List<RID> remaining; |
838 | version_owner.get_owned_list(&remaining); |
839 | if (remaining.size()) { |
840 | ERR_PRINT(itos(remaining.size()) + " shaders of type " + name + " were never freed" ); |
841 | while (remaining.size()) { |
842 | version_free(remaining.front()->get()); |
843 | remaining.pop_front(); |
844 | } |
845 | } |
846 | } |
847 | |