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