1 | /**************************************************************************/ |
2 | /* shader_gles3.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_gles3.h" |
32 | |
33 | #ifdef GLES3_ENABLED |
34 | |
35 | #include "core/io/compression.h" |
36 | #include "core/io/dir_access.h" |
37 | #include "core/io/file_access.h" |
38 | |
39 | static String _mkid(const String &p_id) { |
40 | String id = "m_" + p_id.replace("__" , "_dus_" ); |
41 | return id.replace("__" , "_dus_" ); //doubleunderscore is reserved in glsl |
42 | } |
43 | |
44 | void ShaderGLES3::_add_stage(const char *p_code, StageType p_stage_type) { |
45 | Vector<String> lines = String(p_code).split("\n" ); |
46 | |
47 | String text; |
48 | |
49 | for (int i = 0; i < lines.size(); i++) { |
50 | String l = lines[i]; |
51 | bool push_chunk = false; |
52 | |
53 | StageTemplate::Chunk chunk; |
54 | |
55 | if (l.begins_with("#GLOBALS" )) { |
56 | switch (p_stage_type) { |
57 | case STAGE_TYPE_VERTEX: |
58 | chunk.type = StageTemplate::Chunk::TYPE_VERTEX_GLOBALS; |
59 | break; |
60 | case STAGE_TYPE_FRAGMENT: |
61 | chunk.type = StageTemplate::Chunk::TYPE_FRAGMENT_GLOBALS; |
62 | break; |
63 | default: { |
64 | } |
65 | } |
66 | |
67 | push_chunk = true; |
68 | } else if (l.begins_with("#MATERIAL_UNIFORMS" )) { |
69 | chunk.type = StageTemplate::Chunk::TYPE_MATERIAL_UNIFORMS; |
70 | push_chunk = true; |
71 | } else if (l.begins_with("#CODE" )) { |
72 | chunk.type = StageTemplate::Chunk::TYPE_CODE; |
73 | push_chunk = true; |
74 | chunk.code = l.replace_first("#CODE" , String()).replace(":" , "" ).strip_edges().to_upper(); |
75 | } else { |
76 | text += l + "\n" ; |
77 | } |
78 | |
79 | if (push_chunk) { |
80 | if (text != String()) { |
81 | StageTemplate::Chunk text_chunk; |
82 | text_chunk.type = StageTemplate::Chunk::TYPE_TEXT; |
83 | text_chunk.text = text.utf8(); |
84 | stage_templates[p_stage_type].chunks.push_back(text_chunk); |
85 | text = String(); |
86 | } |
87 | stage_templates[p_stage_type].chunks.push_back(chunk); |
88 | } |
89 | |
90 | if (text != String()) { |
91 | StageTemplate::Chunk text_chunk; |
92 | text_chunk.type = StageTemplate::Chunk::TYPE_TEXT; |
93 | text_chunk.text = text.utf8(); |
94 | stage_templates[p_stage_type].chunks.push_back(text_chunk); |
95 | text = String(); |
96 | } |
97 | } |
98 | } |
99 | |
100 | void ShaderGLES3::_setup(const char *p_vertex_code, const char *p_fragment_code, const char *p_name, int p_uniform_count, const char **p_uniform_names, int p_ubo_count, const UBOPair *p_ubos, int p_feedback_count, const Feedback *p_feedback, int p_texture_count, const TexUnitPair *p_tex_units, int p_specialization_count, const Specialization *p_specializations, int p_variant_count, const char **p_variants) { |
101 | name = p_name; |
102 | |
103 | if (p_vertex_code) { |
104 | _add_stage(p_vertex_code, STAGE_TYPE_VERTEX); |
105 | } |
106 | if (p_fragment_code) { |
107 | _add_stage(p_fragment_code, STAGE_TYPE_FRAGMENT); |
108 | } |
109 | |
110 | uniform_names = p_uniform_names; |
111 | uniform_count = p_uniform_count; |
112 | ubo_pairs = p_ubos; |
113 | ubo_count = p_ubo_count; |
114 | texunit_pairs = p_tex_units; |
115 | texunit_pair_count = p_texture_count; |
116 | specializations = p_specializations; |
117 | specialization_count = p_specialization_count; |
118 | specialization_default_mask = 0; |
119 | for (int i = 0; i < specialization_count; i++) { |
120 | if (specializations[i].default_value) { |
121 | specialization_default_mask |= (uint64_t(1) << uint64_t(i)); |
122 | } |
123 | } |
124 | variant_defines = p_variants; |
125 | variant_count = p_variant_count; |
126 | feedbacks = p_feedback; |
127 | feedback_count = p_feedback_count; |
128 | |
129 | StringBuilder tohash; |
130 | /* |
131 | tohash.append("[SpirvCacheKey]"); |
132 | tohash.append(RenderingDevice::get_singleton()->shader_get_spirv_cache_key()); |
133 | tohash.append("[BinaryCacheKey]"); |
134 | tohash.append(RenderingDevice::get_singleton()->shader_get_binary_cache_key()); |
135 | */ |
136 | tohash.append("[Vertex]" ); |
137 | tohash.append(p_vertex_code ? p_vertex_code : "" ); |
138 | tohash.append("[Fragment]" ); |
139 | tohash.append(p_fragment_code ? p_fragment_code : "" ); |
140 | |
141 | base_sha256 = tohash.as_string().sha256_text(); |
142 | } |
143 | |
144 | RID ShaderGLES3::version_create() { |
145 | //initialize() was never called |
146 | ERR_FAIL_COND_V(variant_count == 0, RID()); |
147 | |
148 | Version version; |
149 | return version_owner.make_rid(version); |
150 | } |
151 | |
152 | void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant, const Version *p_version, StageType p_stage_type, uint64_t p_specialization) { |
153 | #ifdef GLES_OVER_GL |
154 | builder.append("#version 330\n" ); |
155 | builder.append("#define USE_GLES_OVER_GL\n" ); |
156 | #else |
157 | builder.append("#version 300 es\n" ); |
158 | #endif |
159 | |
160 | for (int i = 0; i < specialization_count; i++) { |
161 | if (p_specialization & (uint64_t(1) << uint64_t(i))) { |
162 | builder.append("#define " + String(specializations[i].name) + "\n" ); |
163 | } |
164 | } |
165 | if (p_version->uniforms.size()) { |
166 | builder.append("#define MATERIAL_UNIFORMS_USED\n" ); |
167 | } |
168 | for (const KeyValue<StringName, CharString> &E : p_version->code_sections) { |
169 | builder.append(String("#define " ) + String(E.key) + "_CODE_USED\n" ); |
170 | } |
171 | |
172 | builder.append("\n" ); //make sure defines begin at newline |
173 | builder.append(general_defines.get_data()); |
174 | builder.append(variant_defines[p_variant]); |
175 | builder.append("\n" ); |
176 | for (int j = 0; j < p_version->custom_defines.size(); j++) { |
177 | builder.append(p_version->custom_defines[j].get_data()); |
178 | } |
179 | builder.append("\n" ); //make sure defines begin at newline |
180 | |
181 | // Insert multiview extension loading, because it needs to appear before |
182 | // any non-preprocessor code (like the "precision highp..." lines below). |
183 | builder.append("#ifdef USE_MULTIVIEW\n" ); |
184 | builder.append("#if defined(GL_OVR_multiview2)\n" ); |
185 | builder.append("#extension GL_OVR_multiview2 : require\n" ); |
186 | builder.append("#elif defined(GL_OVR_multiview)\n" ); |
187 | builder.append("#extension GL_OVR_multiview : require\n" ); |
188 | builder.append("#endif\n" ); |
189 | if (p_stage_type == StageType::STAGE_TYPE_VERTEX) { |
190 | builder.append("layout(num_views=2) in;\n" ); |
191 | } |
192 | builder.append("#define ViewIndex gl_ViewID_OVR\n" ); |
193 | builder.append("#define MAX_VIEWS 2\n" ); |
194 | builder.append("#else\n" ); |
195 | builder.append("#define ViewIndex uint(0)\n" ); |
196 | builder.append("#define MAX_VIEWS 1\n" ); |
197 | builder.append("#endif\n" ); |
198 | |
199 | // Default to highp precision unless specified otherwise. |
200 | builder.append("precision highp float;\n" ); |
201 | builder.append("precision highp int;\n" ); |
202 | #ifndef GLES_OVER_GL |
203 | builder.append("precision highp sampler2D;\n" ); |
204 | builder.append("precision highp samplerCube;\n" ); |
205 | builder.append("precision highp sampler2DArray;\n" ); |
206 | #endif |
207 | |
208 | const StageTemplate &stage_template = stage_templates[p_stage_type]; |
209 | for (uint32_t i = 0; i < stage_template.chunks.size(); i++) { |
210 | const StageTemplate::Chunk &chunk = stage_template.chunks[i]; |
211 | switch (chunk.type) { |
212 | case StageTemplate::Chunk::TYPE_MATERIAL_UNIFORMS: { |
213 | builder.append(p_version->uniforms.get_data()); //uniforms (same for vertex and fragment) |
214 | } break; |
215 | case StageTemplate::Chunk::TYPE_VERTEX_GLOBALS: { |
216 | builder.append(p_version->vertex_globals.get_data()); // vertex globals |
217 | } break; |
218 | case StageTemplate::Chunk::TYPE_FRAGMENT_GLOBALS: { |
219 | builder.append(p_version->fragment_globals.get_data()); // fragment globals |
220 | } break; |
221 | case StageTemplate::Chunk::TYPE_CODE: { |
222 | if (p_version->code_sections.has(chunk.code)) { |
223 | builder.append(p_version->code_sections[chunk.code].get_data()); |
224 | } |
225 | } break; |
226 | case StageTemplate::Chunk::TYPE_TEXT: { |
227 | builder.append(chunk.text.get_data()); |
228 | } break; |
229 | } |
230 | } |
231 | } |
232 | |
233 | static void _display_error_with_code(const String &p_error, const String &p_code) { |
234 | int line = 1; |
235 | Vector<String> lines = p_code.split("\n" ); |
236 | |
237 | for (int j = 0; j < lines.size(); j++) { |
238 | print_line(itos(line) + ": " + lines[j]); |
239 | line++; |
240 | } |
241 | |
242 | ERR_PRINT(p_error); |
243 | } |
244 | |
245 | void ShaderGLES3::_get_uniform_locations(Version::Specialization &spec, Version *p_version) { |
246 | glUseProgram(spec.id); |
247 | |
248 | spec.uniform_location.resize(uniform_count); |
249 | for (int i = 0; i < uniform_count; i++) { |
250 | spec.uniform_location[i] = glGetUniformLocation(spec.id, uniform_names[i]); |
251 | } |
252 | |
253 | for (int i = 0; i < texunit_pair_count; i++) { |
254 | GLint loc = glGetUniformLocation(spec.id, texunit_pairs[i].name); |
255 | if (loc >= 0) { |
256 | if (texunit_pairs[i].index < 0) { |
257 | glUniform1i(loc, max_image_units + texunit_pairs[i].index); |
258 | } else { |
259 | glUniform1i(loc, texunit_pairs[i].index); |
260 | } |
261 | } |
262 | } |
263 | |
264 | for (int i = 0; i < ubo_count; i++) { |
265 | GLint loc = glGetUniformBlockIndex(spec.id, ubo_pairs[i].name); |
266 | if (loc >= 0) { |
267 | glUniformBlockBinding(spec.id, loc, ubo_pairs[i].index); |
268 | } |
269 | } |
270 | // textures |
271 | int texture_index = 0; |
272 | for (uint32_t i = 0; i < p_version->texture_uniforms.size(); i++) { |
273 | String native_uniform_name = _mkid(p_version->texture_uniforms[i].name); |
274 | GLint location = glGetUniformLocation(spec.id, (native_uniform_name).ascii().get_data()); |
275 | Vector<int32_t> texture_uniform_bindings; |
276 | int texture_count = p_version->texture_uniforms[i].array_size; |
277 | for (int j = 0; j < texture_count; j++) { |
278 | texture_uniform_bindings.append(texture_index + base_texture_index); |
279 | texture_index++; |
280 | } |
281 | glUniform1iv(location, texture_uniform_bindings.size(), texture_uniform_bindings.ptr()); |
282 | } |
283 | |
284 | glUseProgram(0); |
285 | } |
286 | |
287 | void ShaderGLES3::_compile_specialization(Version::Specialization &spec, uint32_t p_variant, Version *p_version, uint64_t p_specialization) { |
288 | spec.id = glCreateProgram(); |
289 | spec.ok = false; |
290 | GLint status; |
291 | |
292 | //vertex stage |
293 | { |
294 | StringBuilder builder; |
295 | _build_variant_code(builder, p_variant, p_version, STAGE_TYPE_VERTEX, p_specialization); |
296 | |
297 | spec.vert_id = glCreateShader(GL_VERTEX_SHADER); |
298 | String builder_string = builder.as_string(); |
299 | CharString cs = builder_string.utf8(); |
300 | const char *cstr = cs.ptr(); |
301 | glShaderSource(spec.vert_id, 1, &cstr, nullptr); |
302 | glCompileShader(spec.vert_id); |
303 | |
304 | glGetShaderiv(spec.vert_id, GL_COMPILE_STATUS, &status); |
305 | if (status == GL_FALSE) { |
306 | GLsizei iloglen; |
307 | glGetShaderiv(spec.vert_id, GL_INFO_LOG_LENGTH, &iloglen); |
308 | |
309 | if (iloglen < 0) { |
310 | glDeleteShader(spec.vert_id); |
311 | glDeleteProgram(spec.id); |
312 | spec.id = 0; |
313 | |
314 | ERR_PRINT("No OpenGL vertex shader compiler log." ); |
315 | } else { |
316 | if (iloglen == 0) { |
317 | iloglen = 4096; // buggy driver (Adreno 220+) |
318 | } |
319 | |
320 | char *ilogmem = (char *)Memory::alloc_static(iloglen + 1); |
321 | ilogmem[iloglen] = '\0'; |
322 | glGetShaderInfoLog(spec.vert_id, iloglen, &iloglen, ilogmem); |
323 | |
324 | String err_string = name + ": Vertex shader compilation failed:\n" ; |
325 | |
326 | err_string += ilogmem; |
327 | |
328 | _display_error_with_code(err_string, builder_string); |
329 | |
330 | Memory::free_static(ilogmem); |
331 | glDeleteShader(spec.vert_id); |
332 | glDeleteProgram(spec.id); |
333 | spec.id = 0; |
334 | } |
335 | |
336 | ERR_FAIL(); |
337 | } |
338 | } |
339 | |
340 | //fragment stage |
341 | { |
342 | StringBuilder builder; |
343 | _build_variant_code(builder, p_variant, p_version, STAGE_TYPE_FRAGMENT, p_specialization); |
344 | |
345 | spec.frag_id = glCreateShader(GL_FRAGMENT_SHADER); |
346 | String builder_string = builder.as_string(); |
347 | CharString cs = builder_string.utf8(); |
348 | const char *cstr = cs.ptr(); |
349 | glShaderSource(spec.frag_id, 1, &cstr, nullptr); |
350 | glCompileShader(spec.frag_id); |
351 | |
352 | glGetShaderiv(spec.frag_id, GL_COMPILE_STATUS, &status); |
353 | if (status == GL_FALSE) { |
354 | GLsizei iloglen; |
355 | glGetShaderiv(spec.frag_id, GL_INFO_LOG_LENGTH, &iloglen); |
356 | |
357 | if (iloglen < 0) { |
358 | glDeleteShader(spec.frag_id); |
359 | glDeleteProgram(spec.id); |
360 | spec.id = 0; |
361 | |
362 | ERR_PRINT("No OpenGL fragment shader compiler log." ); |
363 | } else { |
364 | if (iloglen == 0) { |
365 | iloglen = 4096; // buggy driver (Adreno 220+) |
366 | } |
367 | |
368 | char *ilogmem = (char *)Memory::alloc_static(iloglen + 1); |
369 | ilogmem[iloglen] = '\0'; |
370 | glGetShaderInfoLog(spec.frag_id, iloglen, &iloglen, ilogmem); |
371 | |
372 | String err_string = name + ": Fragment shader compilation failed:\n" ; |
373 | |
374 | err_string += ilogmem; |
375 | |
376 | _display_error_with_code(err_string, builder_string); |
377 | |
378 | Memory::free_static(ilogmem); |
379 | glDeleteShader(spec.frag_id); |
380 | glDeleteProgram(spec.id); |
381 | spec.id = 0; |
382 | } |
383 | |
384 | ERR_FAIL(); |
385 | } |
386 | } |
387 | |
388 | glAttachShader(spec.id, spec.frag_id); |
389 | glAttachShader(spec.id, spec.vert_id); |
390 | |
391 | // If feedback exists, set it up. |
392 | |
393 | if (feedback_count) { |
394 | Vector<const char *> feedback; |
395 | for (int i = 0; i < feedback_count; i++) { |
396 | if (feedbacks[i].specialization == 0 || (feedbacks[i].specialization & p_specialization)) { |
397 | // Specialization for this feedback is enabled |
398 | feedback.push_back(feedbacks[i].name); |
399 | } |
400 | } |
401 | |
402 | if (feedback.size()) { |
403 | glTransformFeedbackVaryings(spec.id, feedback.size(), feedback.ptr(), GL_INTERLEAVED_ATTRIBS); |
404 | } |
405 | } |
406 | |
407 | glLinkProgram(spec.id); |
408 | |
409 | glGetProgramiv(spec.id, GL_LINK_STATUS, &status); |
410 | if (status == GL_FALSE) { |
411 | GLsizei iloglen; |
412 | glGetProgramiv(spec.id, GL_INFO_LOG_LENGTH, &iloglen); |
413 | |
414 | if (iloglen < 0) { |
415 | glDeleteShader(spec.frag_id); |
416 | glDeleteShader(spec.vert_id); |
417 | glDeleteProgram(spec.id); |
418 | spec.id = 0; |
419 | |
420 | ERR_PRINT("No OpenGL program link log. Something is wrong." ); |
421 | ERR_FAIL(); |
422 | } |
423 | |
424 | if (iloglen == 0) { |
425 | iloglen = 4096; // buggy driver (Adreno 220+) |
426 | } |
427 | |
428 | char *ilogmem = (char *)Memory::alloc_static(iloglen + 1); |
429 | ilogmem[iloglen] = '\0'; |
430 | glGetProgramInfoLog(spec.id, iloglen, &iloglen, ilogmem); |
431 | |
432 | String err_string = name + ": Program linking failed:\n" ; |
433 | |
434 | err_string += ilogmem; |
435 | |
436 | _display_error_with_code(err_string, String()); |
437 | |
438 | Memory::free_static(ilogmem); |
439 | glDeleteShader(spec.frag_id); |
440 | glDeleteShader(spec.vert_id); |
441 | glDeleteProgram(spec.id); |
442 | spec.id = 0; |
443 | |
444 | ERR_FAIL(); |
445 | } |
446 | |
447 | _get_uniform_locations(spec, p_version); |
448 | |
449 | spec.ok = true; |
450 | } |
451 | |
452 | RS::ShaderNativeSourceCode ShaderGLES3::version_get_native_source_code(RID p_version) { |
453 | Version *version = version_owner.get_or_null(p_version); |
454 | RS::ShaderNativeSourceCode source_code; |
455 | ERR_FAIL_NULL_V(version, source_code); |
456 | |
457 | source_code.versions.resize(variant_count); |
458 | |
459 | for (int i = 0; i < source_code.versions.size(); i++) { |
460 | //vertex stage |
461 | |
462 | { |
463 | StringBuilder builder; |
464 | _build_variant_code(builder, i, version, STAGE_TYPE_VERTEX, specialization_default_mask); |
465 | |
466 | RS::ShaderNativeSourceCode::Version::Stage stage; |
467 | stage.name = "vertex" ; |
468 | stage.code = builder.as_string(); |
469 | |
470 | source_code.versions.write[i].stages.push_back(stage); |
471 | } |
472 | |
473 | //fragment stage |
474 | { |
475 | StringBuilder builder; |
476 | _build_variant_code(builder, i, version, STAGE_TYPE_FRAGMENT, specialization_default_mask); |
477 | |
478 | RS::ShaderNativeSourceCode::Version::Stage stage; |
479 | stage.name = "fragment" ; |
480 | stage.code = builder.as_string(); |
481 | |
482 | source_code.versions.write[i].stages.push_back(stage); |
483 | } |
484 | } |
485 | |
486 | return source_code; |
487 | } |
488 | |
489 | String ShaderGLES3::_version_get_sha1(Version *p_version) const { |
490 | StringBuilder hash_build; |
491 | |
492 | hash_build.append("[uniforms]" ); |
493 | hash_build.append(p_version->uniforms.get_data()); |
494 | hash_build.append("[vertex_globals]" ); |
495 | hash_build.append(p_version->vertex_globals.get_data()); |
496 | hash_build.append("[fragment_globals]" ); |
497 | hash_build.append(p_version->fragment_globals.get_data()); |
498 | |
499 | Vector<StringName> code_sections; |
500 | for (const KeyValue<StringName, CharString> &E : p_version->code_sections) { |
501 | code_sections.push_back(E.key); |
502 | } |
503 | code_sections.sort_custom<StringName::AlphCompare>(); |
504 | |
505 | for (int i = 0; i < code_sections.size(); i++) { |
506 | hash_build.append(String("[code:" ) + String(code_sections[i]) + "]" ); |
507 | hash_build.append(p_version->code_sections[code_sections[i]].get_data()); |
508 | } |
509 | for (int i = 0; i < p_version->custom_defines.size(); i++) { |
510 | hash_build.append("[custom_defines:" + itos(i) + "]" ); |
511 | hash_build.append(p_version->custom_defines[i].get_data()); |
512 | } |
513 | |
514 | return hash_build.as_string().sha1_text(); |
515 | } |
516 | |
517 | #ifndef WEB_ENABLED // not supported in webgl |
518 | static const char * = "GLSC" ; |
519 | static const uint32_t cache_file_version = 3; |
520 | #endif |
521 | |
522 | bool ShaderGLES3::_load_from_cache(Version *p_version) { |
523 | #ifdef WEB_ENABLED // not supported in webgl |
524 | return false; |
525 | #else |
526 | #ifdef GLES_OVER_GL |
527 | if (glProgramBinary == NULL) { // ARB_get_program_binary extension not available |
528 | return false; |
529 | } |
530 | #endif |
531 | String sha1 = _version_get_sha1(p_version); |
532 | String path = shader_cache_dir.path_join(name).path_join(base_sha256).path_join(sha1) + ".cache" ; |
533 | |
534 | Ref<FileAccess> f = FileAccess::open(path, FileAccess::READ); |
535 | if (f.is_null()) { |
536 | return false; |
537 | } |
538 | |
539 | char [5] = {}; |
540 | f->get_buffer((uint8_t *)header, 4); |
541 | ERR_FAIL_COND_V(header != String(shader_file_header), false); |
542 | |
543 | uint32_t file_version = f->get_32(); |
544 | if (file_version != cache_file_version) { |
545 | return false; // wrong version |
546 | } |
547 | |
548 | int cache_variant_count = static_cast<int>(f->get_32()); |
549 | ERR_FAIL_COND_V_MSG(cache_variant_count != this->variant_count, false, "shader cache variant count mismatch, expected " + itos(this->variant_count) + " got " + itos(cache_variant_count)); //should not happen but check |
550 | |
551 | LocalVector<OAHashMap<uint64_t, Version::Specialization>> variants; |
552 | for (int i = 0; i < cache_variant_count; i++) { |
553 | uint32_t cache_specialization_count = f->get_32(); |
554 | OAHashMap<uint64_t, Version::Specialization> variant; |
555 | for (uint32_t j = 0; j < cache_specialization_count; j++) { |
556 | uint64_t specialization_key = f->get_64(); |
557 | uint32_t variant_size = f->get_32(); |
558 | if (variant_size == 0) { |
559 | continue; |
560 | } |
561 | uint32_t variant_format = f->get_32(); |
562 | Vector<uint8_t> variant_bytes; |
563 | variant_bytes.resize(variant_size); |
564 | |
565 | uint32_t br = f->get_buffer(variant_bytes.ptrw(), variant_size); |
566 | |
567 | ERR_FAIL_COND_V(br != variant_size, false); |
568 | |
569 | Version::Specialization specialization; |
570 | |
571 | specialization.id = glCreateProgram(); |
572 | glProgramBinary(specialization.id, variant_format, variant_bytes.ptr(), variant_bytes.size()); |
573 | |
574 | GLint link_status = 0; |
575 | glGetProgramiv(specialization.id, GL_LINK_STATUS, &link_status); |
576 | if (link_status != GL_TRUE) { |
577 | WARN_PRINT_ONCE("Failed to load cached shader, recompiling." ); |
578 | return false; |
579 | } |
580 | |
581 | _get_uniform_locations(specialization, p_version); |
582 | |
583 | specialization.ok = true; |
584 | |
585 | variant.insert(specialization_key, specialization); |
586 | } |
587 | variants.push_back(variant); |
588 | } |
589 | p_version->variants = variants; |
590 | |
591 | return true; |
592 | #endif // WEB_ENABLED |
593 | } |
594 | |
595 | void ShaderGLES3::_save_to_cache(Version *p_version) { |
596 | #ifdef WEB_ENABLED // not supported in webgl |
597 | return; |
598 | #else |
599 | #ifdef GLES_OVER_GL |
600 | if (glGetProgramBinary == NULL) { // ARB_get_program_binary extension not available |
601 | return; |
602 | } |
603 | #endif |
604 | String sha1 = _version_get_sha1(p_version); |
605 | String path = shader_cache_dir.path_join(name).path_join(base_sha256).path_join(sha1) + ".cache" ; |
606 | |
607 | Error error; |
608 | Ref<FileAccess> f = FileAccess::open(path, FileAccess::WRITE, &error); |
609 | ERR_FAIL_COND(f.is_null()); |
610 | f->store_buffer((const uint8_t *)shader_file_header, 4); |
611 | f->store_32(cache_file_version); |
612 | f->store_32(variant_count); |
613 | |
614 | for (int i = 0; i < variant_count; i++) { |
615 | int cache_specialization_count = p_version->variants[i].get_num_elements(); |
616 | f->store_32(cache_specialization_count); |
617 | |
618 | for (OAHashMap<uint64_t, ShaderGLES3::Version::Specialization>::Iterator it = p_version->variants[i].iter(); it.valid; it = p_version->variants[i].next_iter(it)) { |
619 | const uint64_t specialization_key = *it.key; |
620 | f->store_64(specialization_key); |
621 | |
622 | const Version::Specialization *specialization = it.value; |
623 | if (specialization == nullptr) { |
624 | f->store_32(0); |
625 | continue; |
626 | } |
627 | GLint program_size = 0; |
628 | glGetProgramiv(specialization->id, GL_PROGRAM_BINARY_LENGTH, &program_size); |
629 | if (program_size == 0) { |
630 | f->store_32(0); |
631 | continue; |
632 | } |
633 | PackedByteArray compiled_program; |
634 | compiled_program.resize(program_size); |
635 | GLenum binary_format = 0; |
636 | glGetProgramBinary(specialization->id, program_size, nullptr, &binary_format, compiled_program.ptrw()); |
637 | if (program_size != compiled_program.size()) { |
638 | f->store_32(0); |
639 | continue; |
640 | } |
641 | f->store_32(program_size); |
642 | f->store_32(binary_format); |
643 | f->store_buffer(compiled_program.ptr(), compiled_program.size()); |
644 | } |
645 | } |
646 | #endif // WEB_ENABLED |
647 | } |
648 | |
649 | void ShaderGLES3::_clear_version(Version *p_version) { |
650 | // Variants not compiled yet, just return |
651 | if (p_version->variants.size() == 0) { |
652 | return; |
653 | } |
654 | |
655 | for (int i = 0; i < variant_count; i++) { |
656 | for (OAHashMap<uint64_t, Version::Specialization>::Iterator it = p_version->variants[i].iter(); it.valid; it = p_version->variants[i].next_iter(it)) { |
657 | if (it.value->id != 0) { |
658 | glDeleteShader(it.value->vert_id); |
659 | glDeleteShader(it.value->frag_id); |
660 | glDeleteProgram(it.value->id); |
661 | } |
662 | } |
663 | } |
664 | |
665 | p_version->variants.clear(); |
666 | } |
667 | |
668 | void ShaderGLES3::_initialize_version(Version *p_version) { |
669 | ERR_FAIL_COND(p_version->variants.size() > 0); |
670 | if (shader_cache_dir_valid && _load_from_cache(p_version)) { |
671 | return; |
672 | } |
673 | p_version->variants.reserve(variant_count); |
674 | for (int i = 0; i < variant_count; i++) { |
675 | OAHashMap<uint64_t, Version::Specialization> variant; |
676 | p_version->variants.push_back(variant); |
677 | Version::Specialization spec; |
678 | _compile_specialization(spec, i, p_version, specialization_default_mask); |
679 | p_version->variants[i].insert(specialization_default_mask, spec); |
680 | } |
681 | if (shader_cache_dir_valid) { |
682 | _save_to_cache(p_version); |
683 | } |
684 | } |
685 | |
686 | void ShaderGLES3::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, const LocalVector<ShaderGLES3::TextureUniformData> &p_texture_uniforms, bool p_initialize) { |
687 | Version *version = version_owner.get_or_null(p_version); |
688 | ERR_FAIL_NULL(version); |
689 | |
690 | _clear_version(version); //clear if existing |
691 | |
692 | version->vertex_globals = p_vertex_globals.utf8(); |
693 | version->fragment_globals = p_fragment_globals.utf8(); |
694 | version->uniforms = p_uniforms.utf8(); |
695 | version->code_sections.clear(); |
696 | version->texture_uniforms = p_texture_uniforms; |
697 | for (const KeyValue<String, String> &E : p_code) { |
698 | version->code_sections[StringName(E.key.to_upper())] = E.value.utf8(); |
699 | } |
700 | |
701 | version->custom_defines.clear(); |
702 | for (int i = 0; i < p_custom_defines.size(); i++) { |
703 | version->custom_defines.push_back(p_custom_defines[i].utf8()); |
704 | } |
705 | |
706 | if (p_initialize) { |
707 | _initialize_version(version); |
708 | } |
709 | } |
710 | |
711 | bool ShaderGLES3::version_is_valid(RID p_version) { |
712 | Version *version = version_owner.get_or_null(p_version); |
713 | return version != nullptr; |
714 | } |
715 | |
716 | bool ShaderGLES3::version_free(RID p_version) { |
717 | if (version_owner.owns(p_version)) { |
718 | Version *version = version_owner.get_or_null(p_version); |
719 | _clear_version(version); |
720 | version_owner.free(p_version); |
721 | } else { |
722 | return false; |
723 | } |
724 | |
725 | return true; |
726 | } |
727 | |
728 | bool ShaderGLES3::shader_cache_cleanup_on_start = false; |
729 | |
730 | ShaderGLES3::ShaderGLES3() { |
731 | } |
732 | |
733 | void ShaderGLES3::initialize(const String &p_general_defines, int p_base_texture_index) { |
734 | general_defines = p_general_defines.utf8(); |
735 | base_texture_index = p_base_texture_index; |
736 | |
737 | _init(); |
738 | |
739 | if (shader_cache_dir != String()) { |
740 | StringBuilder hash_build; |
741 | |
742 | hash_build.append("[base_hash]" ); |
743 | hash_build.append(base_sha256); |
744 | hash_build.append("[general_defines]" ); |
745 | hash_build.append(general_defines.get_data()); |
746 | for (int i = 0; i < variant_count; i++) { |
747 | hash_build.append("[variant_defines:" + itos(i) + "]" ); |
748 | hash_build.append(variant_defines[i]); |
749 | } |
750 | |
751 | base_sha256 = hash_build.as_string().sha256_text(); |
752 | |
753 | Ref<DirAccess> d = DirAccess::open(shader_cache_dir); |
754 | ERR_FAIL_COND(d.is_null()); |
755 | if (d->change_dir(name) != OK) { |
756 | Error err = d->make_dir(name); |
757 | ERR_FAIL_COND(err != OK); |
758 | d->change_dir(name); |
759 | } |
760 | |
761 | //erase other versions? |
762 | if (shader_cache_cleanup_on_start) { |
763 | } |
764 | // |
765 | if (d->change_dir(base_sha256) != OK) { |
766 | Error err = d->make_dir(base_sha256); |
767 | ERR_FAIL_COND(err != OK); |
768 | } |
769 | shader_cache_dir_valid = true; |
770 | |
771 | print_verbose("Shader '" + name + "' SHA256: " + base_sha256); |
772 | } |
773 | |
774 | glGetInteger64v(GL_MAX_TEXTURE_IMAGE_UNITS, &max_image_units); |
775 | } |
776 | |
777 | void ShaderGLES3::set_shader_cache_dir(const String &p_dir) { |
778 | shader_cache_dir = p_dir; |
779 | } |
780 | |
781 | void ShaderGLES3::set_shader_cache_save_compressed(bool p_enable) { |
782 | shader_cache_save_compressed = p_enable; |
783 | } |
784 | |
785 | void ShaderGLES3::set_shader_cache_save_compressed_zstd(bool p_enable) { |
786 | shader_cache_save_compressed_zstd = p_enable; |
787 | } |
788 | |
789 | void ShaderGLES3::set_shader_cache_save_debug(bool p_enable) { |
790 | shader_cache_save_debug = p_enable; |
791 | } |
792 | |
793 | String ShaderGLES3::shader_cache_dir; |
794 | bool ShaderGLES3::shader_cache_save_compressed = true; |
795 | bool ShaderGLES3::shader_cache_save_compressed_zstd = true; |
796 | bool ShaderGLES3::shader_cache_save_debug = true; |
797 | |
798 | ShaderGLES3::~ShaderGLES3() { |
799 | List<RID> remaining; |
800 | version_owner.get_owned_list(&remaining); |
801 | if (remaining.size()) { |
802 | ERR_PRINT(itos(remaining.size()) + " shaders of type " + name + " were never freed" ); |
803 | while (remaining.size()) { |
804 | version_free(remaining.front()->get()); |
805 | remaining.pop_front(); |
806 | } |
807 | } |
808 | } |
809 | #endif |
810 | |