From 5ac5b23d57f80732a82a7fa81401263ff7757c5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20J=2E=20Est=C3=A9banez?= Date: Wed, 17 Feb 2021 13:12:38 +0100 Subject: [PATCH 1/3] Avoid the need for copy assignment in HashMap key/data types --- core/hash_map.h | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/core/hash_map.h b/core/hash_map.h index 7ba8757c54b7..0ad529d27a11 100644 --- a/core/hash_map.h +++ b/core/hash_map.h @@ -62,7 +62,9 @@ class HashMap { TKey key; TData data; - Pair() {} + Pair(const TKey &p_key) : + key(p_key), + data() {} Pair(const TKey &p_key, const TData &p_data) : key(p_key), data(p_data) { @@ -90,6 +92,11 @@ class HashMap { const TData &value() const { return pair.value(); } + + Element(const TKey &p_key) : + pair(p_key) {} + Element(const Element &p_other) : + pair(p_other.pair.key, p_other.pair.data) {} }; private: @@ -192,14 +199,12 @@ class HashMap { Element *create_element(const TKey &p_key) { /* if element doesn't exist, create it */ - Element *e = memnew(Element); + Element *e = memnew(Element(p_key)); ERR_FAIL_COND_V_MSG(!e, nullptr, "Out of memory."); uint32_t hash = Hasher::hash(p_key); uint32_t index = hash & ((1 << hash_table_power) - 1); e->next = hash_table[index]; e->hash = hash; - e->pair.key = p_key; - e->pair.data = TData(); hash_table[index] = e; elements++; @@ -228,9 +233,7 @@ class HashMap { const Element *e = p_t.hash_table[i]; while (e) { - Element *le = memnew(Element); /* local element */ - - *le = *e; /* copy data */ + Element *le = memnew(Element(*e)); /* local element */ /* add to list and reassign pointers */ le->next = hash_table[i]; From 5bd327ce8ac142ddd8509ec822412be3340d9ac9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20J=2E=20Est=C3=A9banez?= Date: Mon, 22 Feb 2021 22:54:12 +0100 Subject: [PATCH 2/3] Implement get_cache_path() for iOS, improve for Android, and fix it for Windows --- platform/android/os_android.cpp | 7 +++++-- platform/android/os_android.h | 1 + platform/iphone/app_delegate.mm | 7 +++++-- platform/iphone/godot_iphone.mm | 4 ++-- platform/iphone/os_iphone.h | 4 +++- platform/iphone/os_iphone.mm | 7 ++++++- platform/windows/os_windows.cpp | 5 ++++- 7 files changed, 26 insertions(+), 9 deletions(-) diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index ec3d12102515..8088c2e850bf 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -452,10 +452,13 @@ String OS_Android::get_user_data_dir() const { } String OS_Android::get_cache_path() const { + if (cache_dir_cache != String()) + return cache_dir_cache; + String cache_dir = godot_io_java->get_cache_dir(); if (cache_dir != "") { - cache_dir = _remove_symlink(cache_dir); - return cache_dir; + cache_dir_cache = _remove_symlink(cache_dir); + return cache_dir_cache; } return "."; } diff --git a/platform/android/os_android.h b/platform/android/os_android.h index 7f369d893347..6fd20c4b23cd 100644 --- a/platform/android/os_android.h +++ b/platform/android/os_android.h @@ -50,6 +50,7 @@ class OS_Android : public OS_Unix { VisualServer *visual_server; mutable String data_dir_cache; + mutable String cache_dir_cache; AudioDriverOpenSL audio_driver_android; diff --git a/platform/iphone/app_delegate.mm b/platform/iphone/app_delegate.mm index d7d815d835dc..1a6381a1a7cd 100644 --- a/platform/iphone/app_delegate.mm +++ b/platform/iphone/app_delegate.mm @@ -44,7 +44,7 @@ extern int gargc; extern char **gargv; -extern int iphone_main(int, char **, String); +extern int iphone_main(int, char **, String, String); extern void iphone_finish(); @implementation AppDelegate @@ -63,8 +63,11 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; + paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, + NSUserDomainMask, YES); + NSString *cacheDirectory = [paths objectAtIndex:0]; - int err = iphone_main(gargc, gargv, String::utf8([documentsDirectory UTF8String])); + int err = iphone_main(gargc, gargv, String::utf8([documentsDirectory UTF8String]), String::utf8([cacheDirectory UTF8String])); if (err != 0) { // bail, things did not go very well for us, should probably output a message on screen with our error code... exit(0); diff --git a/platform/iphone/godot_iphone.mm b/platform/iphone/godot_iphone.mm index 78676fe00967..e030ae2df197 100644 --- a/platform/iphone/godot_iphone.mm +++ b/platform/iphone/godot_iphone.mm @@ -70,7 +70,7 @@ int add_cmdline(int p_argc, char **p_args) { return p_argc; } -int iphone_main(int argc, char **argv, String data_dir) { +int iphone_main(int argc, char **argv, String data_dir, String cache_dir) { size_t len = strlen(argv[0]); while (len--) { @@ -90,7 +90,7 @@ int iphone_main(int argc, char **argv, String data_dir) { char cwd[512]; getcwd(cwd, sizeof(cwd)); printf("cwd %s\n", cwd); - os = new OSIPhone(data_dir); + os = new OSIPhone(data_dir, cache_dir); char *fargv[64]; for (int i = 0; i < argc; i++) { diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h index ad9a8eed72bf..a7a70c335281 100644 --- a/platform/iphone/os_iphone.h +++ b/platform/iphone/os_iphone.h @@ -81,6 +81,7 @@ class OSIPhone : public OS_Unix { void set_data_dir(String p_dir); String data_dir; + String cache_dir; InputDefault *input; @@ -93,7 +94,7 @@ class OSIPhone : public OS_Unix { public: static OSIPhone *get_singleton(); - OSIPhone(String p_data_dir); + OSIPhone(String p_data_dir, String p_cache_dir); ~OSIPhone(); bool iterate(); @@ -115,6 +116,7 @@ class OSIPhone : public OS_Unix { Error shell_open(String p_uri); String get_user_data_dir() const; + String get_cache_path() const; String get_locale() const; diff --git a/platform/iphone/os_iphone.mm b/platform/iphone/os_iphone.mm index 21fed4a187b5..c115bc099766 100644 --- a/platform/iphone/os_iphone.mm +++ b/platform/iphone/os_iphone.mm @@ -494,6 +494,10 @@ void register_dynamic_symbol(char *name, void *address) { return String::utf8([text UTF8String]); } +String OSIPhone::get_cache_path() const { + return cache_dir; +} + String OSIPhone::get_model_name() const { String model = ios->get_model(); if (model != "") { @@ -668,7 +672,7 @@ void add_ios_init_callback(init_callback cb) { } } -OSIPhone::OSIPhone(String p_data_dir) { +OSIPhone::OSIPhone(String p_data_dir, String p_cache_dir) { for (int i = 0; i < ios_init_callbacks_count; ++i) { ios_init_callbacks[i](); } @@ -683,6 +687,7 @@ void add_ios_init_callback(init_callback cb) { // can't call set_data_dir from here, since it requires DirAccess // which is initialized in initialize_core data_dir = p_data_dir; + cache_dir = p_cache_dir; Vector loggers; loggers.push_back(memnew(SyslogLogger)); diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 6f0cfea97345..9f3477e59903 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -3387,9 +3387,12 @@ String OS_Windows::get_cache_path() const { if (get_environment("XDG_CACHE_HOME").is_abs_path()) { return get_environment("XDG_CACHE_HOME").replace("\\", "/"); } else { - WARN_PRINT_ONCE("`XDG_CACHE_HOME` is a relative path. Ignoring its value and falling back to `%TEMP%` or `get_config_path()` per the XDG Base Directory specification."); + WARN_PRINT_ONCE("`XDG_CACHE_HOME` is a relative path. Ignoring its value and falling back to `%LOCALAPPDATA%\\cache`, `%TEMP%` or `get_config_path()` per the XDG Base Directory specification."); } } + if (has_environment("LOCALAPPDATA")) { + return get_environment("LOCALAPPDATA").replace("\\", "/").plus_file("cache"); + } if (has_environment("TEMP")) { return get_environment("TEMP").replace("\\", "/"); } From 23f9895079bd042182757ea6a89857ea43998dd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20J=2E=20Est=C3=A9banez?= Date: Mon, 22 Feb 2021 22:52:00 +0100 Subject: [PATCH 3/3] Add shader async. compilation + caching for GL ES 3 --- core/os/os.cpp | 6 + core/os/os.h | 3 + core/threaded_callable_queue.h | 133 ++++ doc/classes/ProjectSettings.xml | 37 + doc/classes/SpatialMaterial.xml | 14 + doc/classes/VisualServer.xml | 10 + drivers/dummy/rasterizer_dummy.h | 3 + drivers/gles2/rasterizer_storage_gles2.h | 3 + drivers/gles3/rasterizer_gles3.cpp | 15 +- drivers/gles3/rasterizer_scene_gles3.cpp | 197 +++++- drivers/gles3/rasterizer_scene_gles3.h | 19 +- drivers/gles3/rasterizer_storage_gles3.cpp | 119 +++- drivers/gles3/rasterizer_storage_gles3.h | 16 + drivers/gles3/shader_cache_gles3.cpp | 196 ++++++ drivers/gles3/shader_cache_gles3.h | 60 ++ drivers/gles3/shader_gles3.cpp | 772 +++++++++++++++------ drivers/gles3/shader_gles3.h | 128 +++- drivers/gles3/shaders/scene.glsl | 22 + editor/editor_export.cpp | 5 + editor/editor_export.h | 1 + editor/editor_node.cpp | 16 + editor/editor_node.h | 1 + editor/editor_run.cpp | 15 + editor/editor_run.h | 4 + editor/editor_run_native.cpp | 12 + editor/editor_run_native.h | 4 + editor/editor_settings.cpp | 1 - platform/iphone/display_layer.mm | 7 + platform/iphone/os_iphone.h | 6 + platform/iphone/os_iphone.mm | 20 + platform/osx/os_osx.h | 4 + platform/osx/os_osx.mm | 15 + platform/windows/context_gl_windows.cpp | 18 + platform/windows/context_gl_windows.h | 5 + platform/windows/os_windows.cpp | 18 + platform/windows/os_windows.h | 3 + platform/x11/context_gl_x11.cpp | 21 + platform/x11/context_gl_x11.h | 4 + platform/x11/os_x11.cpp | 18 + platform/x11/os_x11.h | 3 + scene/resources/material.cpp | 49 ++ scene/resources/material.h | 11 + scene/resources/visual_shader.cpp | 1 + servers/visual/rasterizer.h | 3 + servers/visual/shader_types.cpp | 4 + servers/visual/visual_server_raster.h | 2 + servers/visual/visual_server_scene.cpp | 4 + servers/visual/visual_server_wrap_mt.h | 2 + servers/visual_server.cpp | 1 + servers/visual_server.h | 2 + thirdparty/glad/glad.c | 40 +- thirdparty/glad/glad/glad.h | 46 +- 52 files changed, 1887 insertions(+), 232 deletions(-) create mode 100644 core/threaded_callable_queue.h create mode 100644 drivers/gles3/shader_cache_gles3.cpp create mode 100644 drivers/gles3/shader_cache_gles3.h diff --git a/core/os/os.cpp b/core/os/os.cpp index dfeb820b5464..bc0a07564a37 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -711,6 +711,12 @@ const char *OS::get_video_driver_name(int p_driver) const { } } +bool OS::is_offscreen_gl_available() const { + return false; +} + +void OS::set_offscreen_gl_current(bool p_current) {} + int OS::get_audio_driver_count() const { return AudioDriverManager::get_driver_count(); } diff --git a/core/os/os.h b/core/os/os.h index cd763a61c3d6..cb375a86f3f5 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -191,6 +191,9 @@ class OS { virtual const char *get_video_driver_name(int p_driver) const; virtual int get_current_video_driver() const = 0; + virtual bool is_offscreen_gl_available() const; + virtual void set_offscreen_gl_current(bool p_current); + virtual int get_audio_driver_count() const; virtual const char *get_audio_driver_name(int p_driver) const; diff --git a/core/threaded_callable_queue.h b/core/threaded_callable_queue.h new file mode 100644 index 000000000000..334846694032 --- /dev/null +++ b/core/threaded_callable_queue.h @@ -0,0 +1,133 @@ +/*************************************************************************/ +/* threaded_callable_queue.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef THREADED_CALLABLE_QUEUE_H +#define THREADED_CALLABLE_QUEUE_H + +#include "core/local_vector.h" +#include "core/ordered_hash_map.h" +#include "core/os/mutex.h" +#include "core/os/semaphore.h" +#include "core/os/thread.h" + +#include + +template +class ThreadedCallableQueue { +public: + using Job = std::function; + +private: + bool exit; + Thread thread; + BinaryMutex mutex; + Semaphore sem; + OrderedHashMap queue; + + static void _thread_func(void *p_user_data); + +public: + void enqueue(K p_key, Job p_job); + void cancel(K p_key); + + ThreadedCallableQueue(); + ~ThreadedCallableQueue(); +}; + +template +void ThreadedCallableQueue::_thread_func(void *p_user_data) { + ThreadedCallableQueue *self = static_cast(p_user_data); + + while (true) { + self->sem.wait(); + self->mutex.lock(); + if (self->exit) { + self->mutex.unlock(); + break; + } + + typename OrderedHashMap::Element E = self->queue.front(); + // Defense about implementation bugs (excessive posts) + if (!E) { + ERR_PRINT("Semaphore unlocked, the queue is empty. Bug?"); + self->mutex.unlock(); + // --- Defense end + } else { + LocalVector jobs; + jobs.push_back(E.value()); + self->queue.erase(E); + self->mutex.unlock(); + + for (uint32_t i = 0; i < jobs.size(); i++) { + jobs[i](); + } + } + } + + self->mutex.lock(); + for (typename OrderedHashMap::Element E = self->queue.front(); E; E = E.next()) { + Job job = E.value(); + job(); + } + self->mutex.unlock(); +} + +template +void ThreadedCallableQueue::enqueue(K p_key, Job p_job) { + MutexLock lock(mutex); + ERR_FAIL_COND(exit); + ERR_FAIL_COND(queue.has(p_key)); + queue.insert(p_key, p_job); + sem.post(); +} + +template +void ThreadedCallableQueue::cancel(K p_key) { + MutexLock lock(mutex); + ERR_FAIL_COND(exit); + if (queue.erase(p_key)) { + sem.wait(); + } +} + +template +ThreadedCallableQueue::ThreadedCallableQueue() : + exit(false) { + thread.start(&_thread_func, this); +} + +template +ThreadedCallableQueue::~ThreadedCallableQueue() { + exit = true; + sem.post(); + thread.wait_to_finish(); +} + +#endif // THREADED_CALLABLE_QUEUE_H diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index ff0f8782ef5e..bd42a42d5cf2 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -1213,6 +1213,43 @@ If [code]true[/code] and available on the target device, enables high floating point precision for all shader computations in GLES2. [b]Warning:[/b] High floating point precision can be extremely slow on older devices and is often not available at all. Use with caution. + + If [code]true[/code] and available on the target device, enables asynchronous compilation of shaders. + That means that when a shader is first used under some new rendering situation, if it's configured to have a fallback, the game won't stall while it is being compiled. Instead, the fallback will be used and the real shader will be compiled in the background. Once the actual shader is compiled, it will be used the next times it's used to draw a frame. + This setting also enabled asynchronous reconstruction of shaders from cache. This means that if [code]rendering/gles3/shaders/cache_enabled[/code]) is enabled as well, the reconstruction of a cached shader will also be done in the background. + [b]Warning:[/b] Async. compilation is currently only supported for spatial shaders. + + + If [code]true[/code] and available on the target device, a binary representation of shaders under the rendering situations they are used will be written to a cache directory. + This helps, at the cost of some storage space, getting shaders ready the next time they are used after having been unloaded from memory (namely, next startup of the game or next switch to a specific game scene). + + + Only meaningful if [code]rendering/gles3/shaders/async_compile_enabled[/code] is [code]true[/code]. + If [code]true[/code], for every shader that has a fallback, such fallback is used to render instead of the real shader, even it it's already compiled. + This is useful to see how well the fallbacks look in a scene, because normally they may be switching to the real ones too fast to be assessed. + + + Only meaningful if [code]rendering/gles3/shaders/async_compile_enabled[/code] is [code]true[/code]. + If [code]true[/code], shaders of every kind for which asynchronous compilation is supported will be forced to use a 'no render' fallback. + On one hand, this is a quick way to enable async. shader compilation for a project, instead of deciding the proper fallback mode for each shader. + On the other hand, this helps informing that decision process by letting you run the project and noticing where you can't aesthetically afford to have certain shaders totally absent while they are getting ready. + + + Only meaningful if [code]rendering/gles3/shaders/async_compile_enabled[/code] is [code]true[/code]. + This is the maximum number of shaders that can be compiled (or reconstructed from cache) at the same time. + At runtime, while that count is reached, other shaders that can be asynchronously compiled will just use their fallback, without their setup being started until the count gets lower. + This is a way to balance the CPU work between running the game and compiling the shaders. The goal is to have as many asynchronous compiles in flight as possible without impacting the responsiveness of the game, which beyond some point would destroy the benefits of asynchronous compilation. In other words, you may be able to afford that the FPS lowers a bit, and that will already be better than the stalling that synchronous compilation could cause. + The default value is meant to be a reasonable one for desktop platforms, but you are advised to tweak it according to the hardware you are targeting. + + + A very conservative override for [code]rendering/gles3/shaders/max_concurrent_compiles[/code] on mobile. + Depending on the specific devices you are targeting, you may want to raise it. + + + Only meaningful if [code]rendering/gles3/shaders/async_compile_enabled[/code] is [code]true[/code]. + This is for shaders whose fallback mode is set to 'simple'. While they are being set up, the simple shader used to replace them will be modulated by these RGB values. + This is useful to tweak their appearance to the overall lighting of a game. For instance, the albedo-only look of the simple shader may be too bright so you can use this setting to make the simple fallbacks darker so they don't stand out too much. + Max buffer size for blend shapes. Any blend shape bigger than this will not work. diff --git a/doc/classes/SpatialMaterial.xml b/doc/classes/SpatialMaterial.xml index 4627160b6123..a2c3c227bd3a 100644 --- a/doc/classes/SpatialMaterial.xml +++ b/doc/classes/SpatialMaterial.xml @@ -173,6 +173,11 @@ Texture that specifies how much surface emits light at a given point. + + When the project setting [code]rendering/gles3/shaders/async_compile_enabled[/code] is [code]true[/code], this determines how this material must behave in regards to asynchronous shader compilation. + This behaves like a hint, which means that, depending on the features this material uses, using the simple fallback may not be possible. + The default is [constant FALLBACK_MODE_SIMPLE], which is a good starting point for most projects. Feel free to check the other fallback modes. + Forces a conversion of the [member albedo_texture] from sRGB space to linear space. @@ -638,5 +643,14 @@ Smoothly fades the object out based on the object's distance from the camera using a dither approach. Dithering discards pixels based on a set pattern to smoothly fade without enabling transparency. On certain hardware this can be faster than [constant DISTANCE_FADE_PIXEL_ALPHA]. + + This material won't have a fallback. The application will stop to compile its full blown shader when it's used for the first time. + + + Anything with this material applied won't be rendered while this material's shader is being compiled. + + + Anything with this material applied will be rendered with a simple shader + diff --git a/doc/classes/VisualServer.xml b/doc/classes/VisualServer.xml index 87b1f8fd9f00..614be6a005b5 100644 --- a/doc/classes/VisualServer.xml +++ b/doc/classes/VisualServer.xml @@ -2523,6 +2523,16 @@ Sets the default clear color which is used when a specific clear color has not been selected. + + + + + + + In case asynchronous shader compilation is enabled, this disables or re-enabled it. + A project may want to disable it temporarily, for instance, if it wants to ensure shaders are "warmed up" by just rendering a frame of the whole view of a level beneath a loading screen. + + diff --git a/drivers/dummy/rasterizer_dummy.h b/drivers/dummy/rasterizer_dummy.h index 321dbd2b00c1..b665093d6e75 100644 --- a/drivers/dummy/rasterizer_dummy.h +++ b/drivers/dummy/rasterizer_dummy.h @@ -266,6 +266,9 @@ class RasterizerStorageDummy : public RasterizerStorage { void shader_get_custom_defines(RID p_shader, Vector *p_defines) const {} void shader_remove_custom_define(RID p_shader, const String &p_define) {} + void set_forced_sync_shader_compile_enabled(bool p_enabled) {} + bool is_forced_sync_shader_compile_enabled() { return false; } + /* COMMON MATERIAL API */ RID material_create() { return RID(); } diff --git a/drivers/gles2/rasterizer_storage_gles2.h b/drivers/gles2/rasterizer_storage_gles2.h index f96dab26bf7a..9183e01c5952 100644 --- a/drivers/gles2/rasterizer_storage_gles2.h +++ b/drivers/gles2/rasterizer_storage_gles2.h @@ -543,6 +543,9 @@ class RasterizerStorageGLES2 : public RasterizerStorage { virtual void shader_get_custom_defines(RID p_shader, Vector *p_defines) const; virtual void shader_remove_custom_define(RID p_shader, const String &p_define); + void set_forced_sync_shader_compile_enabled(bool p_enabled) {} + bool is_forced_sync_shader_compile_enabled() { return false; } + void _update_shader(Shader *p_shader) const; void update_dirty_shaders(); diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index 447c3382a240..64b24f11ff55 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -207,6 +207,7 @@ void RasterizerGLES3::begin_frame(double frame_step) { storage->frame.time[2] = Math::fmod(time_total, 900); storage->frame.time[3] = Math::fmod(time_total, 60); storage->frame.count++; + storage->frame.shade_compiles_started = 0; storage->frame.delta = frame_step; storage->update_dirty_resources(); @@ -214,6 +215,8 @@ void RasterizerGLES3::begin_frame(double frame_step) { storage->info.render_final = storage->info.render; storage->info.render.reset(); + ShaderGLES3::current_frame = storage->frame.count; + scene->iteration(); } @@ -410,6 +413,8 @@ void RasterizerGLES3::end_frame(bool p_swap_buffers) { } } + ShaderGLES3::advance_async_shaders_compilation(); + if (p_swap_buffers) { OS::get_singleton()->swap_buffers(); } else { @@ -431,6 +436,13 @@ void RasterizerGLES3::make_current() { } void RasterizerGLES3::register_config() { + GLOBAL_DEF("rendering/gles3/shaders/cache_enabled", false); + GLOBAL_DEF("rendering/gles3/shaders/async_compile_enabled", false); + GLOBAL_DEF("rendering/gles3/shaders/max_concurrent_compiles", 4); + GLOBAL_DEF("rendering/gles3/shaders/max_concurrent_compiles.mobile", 1); + GLOBAL_DEF("rendering/gles3/shaders/simple_fallback_modulate", Color(1, 1, 1)); + GLOBAL_DEF("rendering/gles3/shaders/force_no_render_fallback", false); + GLOBAL_DEF("rendering/gles3/shaders/debug_force_use_fallbacks", false); } bool RasterizerGLES3::gl_check_errors() { @@ -484,13 +496,14 @@ RasterizerGLES3::RasterizerGLES3() { storage->canvas = canvas; scene->storage = storage; storage->scene = scene; + ShaderGLES3::shader_compiles_started_this_frame = &storage->frame.shade_compiles_started; time_total = 0; time_scale = 1; } RasterizerGLES3::~RasterizerGLES3() { - memdelete(storage); memdelete(canvas); memdelete(scene); + memdelete(storage); } diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 4a2509c981c8..dec26a2bfc47 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -1051,7 +1051,7 @@ void RasterizerSceneGLES3::gi_probe_instance_set_bounds(RID p_probe, const Vecto //////////////////////////// //////////////////////////// -bool RasterizerSceneGLES3::_setup_material(RasterizerStorageGLES3::Material *p_material, bool p_depth_pass, bool p_alpha_pass) { +bool RasterizerSceneGLES3::_setup_material(RasterizerStorageGLES3::Material *p_material, bool p_depth_pass, bool p_alpha_pass, bool *r_shader_fallen_back) { /* this is handled outside if (p_material->shader->spatial.cull_mode == RasterizerStorageGLES3::Shader::Spatial::CULL_MODE_DISABLED) { glDisable(GL_CULL_FACE); @@ -1105,7 +1105,17 @@ bool RasterizerSceneGLES3::_setup_material(RasterizerStorageGLES3::Material *p_m //material parameters state.scene_shader.set_custom_shader(p_material->shader->custom_code_id); - bool rebind = state.scene_shader.bind(); + bool rebind = state.scene_shader.bind(r_shader_fallen_back); + if (rebind) { + if (*r_shader_fallen_back) { + _transfer_material_to_simple(p_material); + p_material = simple_material_ptr; + } + } else { + if (!state.scene_shader.is_version_valid()) { + return false; + } + } if (p_material->ubo_id) { glBindBufferBase(GL_UNIFORM_BUFFER, 1, p_material->ubo_id); @@ -1243,6 +1253,58 @@ bool RasterizerSceneGLES3::_setup_material(RasterizerStorageGLES3::Material *p_m return rebind; } +void RasterizerSceneGLES3::_transfer_material_to_simple(RasterizerStorageGLES3::Material *p_material) { + // The simple shader can handle these + + Map::Element *param; + param = p_material->params.find(simple_shader_param_names.albedo); + if (!param) { + param = p_material->params.find(simple_shader_param_names.albedo_color); + } + if (param && param->get().get_type() == Variant::COLOR) { + state.scene_shader.set_uniform(SceneShaderGLES3::SIMPLE_FALLBACK_ALBEDO_COLOR, static_cast(param->get())); + } else { + state.scene_shader.set_uniform(SceneShaderGLES3::SIMPLE_FALLBACK_ALBEDO_COLOR, Color(1, 1, 1, 1)); + } + param = p_material->params.find(simple_shader_param_names.uv1_scale); + if (param && param->get().get_type() == Variant::VECTOR3) { + state.scene_shader.set_uniform(SceneShaderGLES3::SIMPLE_FALLBACK_UV1_SCALE, static_cast(param->get())); + } else { + state.scene_shader.set_uniform(SceneShaderGLES3::SIMPLE_FALLBACK_UV1_SCALE, Vector3(1, 1, 1)); + } + param = p_material->params.find(simple_shader_param_names.uv1_offset); + if (param && param->get().get_type() == Variant::VECTOR3) { + state.scene_shader.set_uniform(SceneShaderGLES3::SIMPLE_FALLBACK_UV1_OFFSET, static_cast(param->get())); + } else { + state.scene_shader.set_uniform(SceneShaderGLES3::SIMPLE_FALLBACK_UV1_OFFSET, Vector3()); + } + + // Let's see if we can find the texture that works as albedo in the original material + + int albedo_tex_id = -1; + for (uint32_t i = 0; i < p_material->shader->texture_count; i++) { + if (p_material->shader->texture_hints[i] == ShaderLanguage::ShaderNode::Uniform::HINT_ALBEDO || p_material->shader->texture_hints[i] == ShaderLanguage::ShaderNode::Uniform::HINT_BLACK_ALBEDO) { + albedo_tex_id = i; + break; + } + } + // If we didn't find any albedo texture, pick the first one that is a sampler 2D, since maybe the original shader didn't mark the texture as albedo + if (albedo_tex_id == -1) { + for (uint32_t i = 0; i < p_material->shader->texture_count; i++) { + if (p_material->shader->texture_types[i] == ShaderLanguage::DataType::TYPE_SAMPLER2D) { + albedo_tex_id = i; + break; + } + } + } + + RID albedo_texture; + if (albedo_tex_id != -1) { + albedo_texture = p_material->textures[albedo_tex_id]; + } + simple_material_ptr->textures.ptrw()[0] = albedo_texture; +} + struct RasterizerGLES3Particle { float color[4]; float velocity_active[4]; @@ -2115,17 +2177,26 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_ rebind = true; } + bool shader_fallen_back = false; + uint32_t original_conditional_version = state.scene_shader.get_version(); if (material != prev_material || rebind) { storage->info.render.material_switch_count++; - rebind = _setup_material(material, use_opaque_prepass, p_alpha_pass); + rebind = _setup_material(material, use_opaque_prepass, p_alpha_pass, &shader_fallen_back); if (rebind) { storage->info.render.shader_rebind_count++; + if (shader_fallen_back) { + material = simple_material_ptr; + } + } else { + if (!state.scene_shader.is_version_valid()) { + continue; + } } } - if (!(e->sort_key & SORT_KEY_UNSHADED_FLAG) && !p_directional_add && !p_shadow) { + if (!shader_fallen_back && !(e->sort_key & SORT_KEY_UNSHADED_FLAG) && !p_directional_add && !p_shadow) { _setup_light(e, p_view_transform); } @@ -2140,6 +2211,10 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_ _render_geometry(e); + if (shader_fallen_back) { + state.scene_shader.set_version(original_conditional_version); + } + prev_material = material; prev_base_type = e->instance->base_type; prev_geometry = e->geometry; @@ -5229,6 +5304,50 @@ void RasterizerSceneGLES3::initialize() { state.debug_draw = VS::VIEWPORT_DEBUG_DRAW_DISABLED; glFrontFace(GL_CW); + + if (storage->config.async_compilation_enabled && (storage->shaders.compile_queue || storage->config.parallel_shader_compile_supported)) { + simple_shader = storage->shader_create(); + simple_shader_ptr = storage->shader_owner.get(simple_shader); + Color global_modulate = ProjectSettings::get_singleton()->get("rendering/gles3/shaders/simple_fallback_modulate"); + String global_modulate_str = + "vec3(" + + rtos(global_modulate.r) + ", " + + rtos(global_modulate.g) + ", " + + rtos(global_modulate.b) + ")"; + storage->shader_set_code(simple_shader, + "shader_type spatial;\n" + "\n" + "render_mode unshaded;\n" + "\n" + // Other parameters are passed with better performance as uniforms, instead of via the UBO; texture is more convenient this way + "uniform sampler2D simple_fallback_albedo_tex : hint_albedo;\n" + "\n" + // The actual bodies are in scene.glsl; + // these dummy assignments enable necessary flags; ugly, but more mainteanable + "void vertex() {\n" + " UV = UV;\n" + "}\n" + "\n" + "void fragment() {\n" + " ALBEDO = ALBEDO * " + + global_modulate_str + ";\n" + " ALPHA = ALPHA;\n" + "}\n"); + simple_material = storage->material_create(); + simple_material_ptr = storage->material_owner.get(simple_material); + storage->material_set_shader(simple_material, simple_shader); + + uint32_t simple_code_id = storage->shader_owner.get(simple_shader)->custom_code_id; + state.scene_shader.setup_async_compilation(storage->shaders.compile_queue, storage->config.parallel_shader_compile_supported, simple_code_id, &_decide_shader_fallback_mode); + + simple_shader_param_names.albedo = StringName("albedo"); + simple_shader_param_names.albedo_color = StringName("albedo_color"); + simple_shader_param_names.uv1_scale = StringName("uv1_scale"); + simple_shader_param_names.uv1_offset = StringName("uv1_offset"); + } else { + simple_shader_ptr = nullptr; + simple_material_ptr = nullptr; + } } void RasterizerSceneGLES3::iteration() { @@ -5241,15 +5360,85 @@ void RasterizerSceneGLES3::iteration() { storage->config.use_lightmap_filter_bicubic = GLOBAL_GET("rendering/quality/lightmapping/use_bicubic_sampling"); state.scene_shader.set_conditional(SceneShaderGLES3::USE_LIGHTMAP_FILTER_BICUBIC, storage->config.use_lightmap_filter_bicubic); state.scene_shader.set_conditional(SceneShaderGLES3::VCT_QUALITY_HIGH, GLOBAL_GET("rendering/quality/voxel_cone_tracing/high_quality")); + + static bool first = true; + if (simple_shader_ptr && first) { + first = false; + bool forced_sync_backup = storage->is_forced_sync_shader_compile_enabled(); + storage->set_forced_sync_shader_compile_enabled(true); + _compile_simple_shader_versions(); + storage->set_forced_sync_shader_compile_enabled(forced_sync_backup); + } } void RasterizerSceneGLES3::finalize() { } +static const uint32_t SHADER_FLAGS_FORBID_SIMPLE = (1 << SceneShaderGLES3::OVERRIDE_POSITION); // Others can be ORed +static const uint32_t SHADER_FLAGS_KEPT_FOR_SIMPLE = (1 << SceneShaderGLES3::USE_SKELETON) | (1 << SceneShaderGLES3::USE_INSTANCING) | (1 << SceneShaderGLES3::USE_MULTIPLE_RENDER_TARGETS); +static const uint32_t SHADER_FLAG_COMBOS_FOR_SIMPLE[] = { + 0, + (1 << SceneShaderGLES3::USE_SKELETON), + (1 << SceneShaderGLES3::USE_INSTANCING), + (1 << SceneShaderGLES3::USE_SKELETON) | (1 << SceneShaderGLES3::USE_INSTANCING), + (1 << SceneShaderGLES3::USE_MULTIPLE_RENDER_TARGETS), + (1 << SceneShaderGLES3::USE_SKELETON) | (1 << SceneShaderGLES3::USE_MULTIPLE_RENDER_TARGETS), + (1 << SceneShaderGLES3::USE_INSTANCING) | (1 << SceneShaderGLES3::USE_MULTIPLE_RENDER_TARGETS), + (1 << SceneShaderGLES3::USE_SKELETON) | (1 << SceneShaderGLES3::USE_INSTANCING) | (1 << SceneShaderGLES3::USE_MULTIPLE_RENDER_TARGETS), + UINT32_MAX, +}; +static const uint32_t SHADER_FLAGS_FORCED_FOR_SIMPLE = (1 << SceneShaderGLES3::IS_SIMPLE_FALLBACK) | (1 << SceneShaderGLES3::SHADELESS); + +void RasterizerSceneGLES3::_compile_simple_shader_versions() { + storage->update_dirty_shaders(); + + const uint32_t *combo = SHADER_FLAG_COMBOS_FOR_SIMPLE; + while (*combo != UINT32_MAX) { + uint32_t version = (*combo) | SHADER_FLAGS_FORCED_FOR_SIMPLE; + + state.scene_shader.set_version(version); + state.scene_shader.set_custom_shader(simple_material_ptr->shader->custom_code_id); + state.scene_shader.bind(); + combo++; + } + + state.scene_shader.set_version(0); + state.scene_shader.set_custom_shader(0); +} + +void RasterizerSceneGLES3::_decide_shader_fallback_mode(uint32_t p_conditionals, int p_fallback_mode_hint, int *r_fallback_mode, uint32_t *r_simple_fallback_version) { + switch (p_fallback_mode_hint) { + case ShaderGLES3::FALLBACK_MODE_NONE: { + *r_fallback_mode = ShaderGLES3::FALLBACK_MODE_NONE; + *r_simple_fallback_version = 0; + } break; + case ShaderGLES3::FALLBACK_MODE_NO_RENDER: { + *r_fallback_mode = ShaderGLES3::FALLBACK_MODE_NO_RENDER; + *r_simple_fallback_version = 0; + } break; + case ShaderGLES3::FALLBACK_MODE_SIMPLE: { + if (!(p_conditionals & SHADER_FLAGS_FORBID_SIMPLE)) { + *r_fallback_mode = ShaderGLES3::FALLBACK_MODE_SIMPLE; + *r_simple_fallback_version = (p_conditionals & SHADER_FLAGS_KEPT_FOR_SIMPLE) | SHADER_FLAGS_FORCED_FOR_SIMPLE; + } else { + *r_fallback_mode = ShaderGLES3::FALLBACK_MODE_NONE; + *r_simple_fallback_version = 0; + } + } break; + } +} + RasterizerSceneGLES3::RasterizerSceneGLES3() { } RasterizerSceneGLES3::~RasterizerSceneGLES3() { + if (simple_material.is_valid()) { + memdelete(simple_material.get_data()); + } + if (simple_shader.is_valid()) { + memdelete(simple_shader.get_data()); + } + memdelete(default_material.get_data()); memdelete(default_material_twosided.get_data()); memdelete(default_shader.get_data()); diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index f502670a1120..4d4d7cd02771 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -34,6 +34,8 @@ /* Must come before shaders or the Windows build fails... */ #include "rasterizer_storage_gles3.h" +#include "core/local_vector.h" + #include "drivers/gles3/shaders/cube_to_dp.glsl.gen.h" #include "drivers/gles3/shaders/effect_blur.glsl.gen.h" #include "drivers/gles3/shaders/exposure.glsl.gen.h" @@ -87,6 +89,17 @@ class RasterizerSceneGLES3 : public RasterizerScene { RID default_overdraw_material; RID default_overdraw_shader; + RID simple_shader; + RasterizerStorageGLES3::Shader *simple_shader_ptr; + RID simple_material; + RasterizerStorageGLES3::Material *simple_material_ptr; + struct { + StringName albedo; + StringName albedo_color; + StringName uv1_scale; + StringName uv1_offset; + } simple_shader_param_names; + RasterizerStorageGLES3 *storage; Vector exposure_shrink; @@ -820,7 +833,8 @@ class RasterizerSceneGLES3 : public RasterizerScene { _FORCE_INLINE_ void _set_cull(bool p_front, bool p_disabled, bool p_reverse_cull); - _FORCE_INLINE_ bool _setup_material(RasterizerStorageGLES3::Material *p_material, bool p_depth_pass, bool p_alpha_pass); + _FORCE_INLINE_ bool _setup_material(RasterizerStorageGLES3::Material *p_material, bool p_depth_pass, bool p_alpha_pass, bool *r_shader_fallen_back); + _FORCE_INLINE_ void _transfer_material_to_simple(RasterizerStorageGLES3::Material *p_material); _FORCE_INLINE_ void _setup_geometry(RenderList::Element *e, const Transform &p_view_transform); _FORCE_INLINE_ void _render_geometry(RenderList::Element *e); void _setup_light(RenderList::Element *e, const Transform &p_view_transform); @@ -858,6 +872,9 @@ class RasterizerSceneGLES3 : public RasterizerScene { virtual void set_scene_pass(uint64_t p_pass); virtual void set_debug_draw_mode(VS::ViewportDebugDraw p_debug_draw); + static void _decide_shader_fallback_mode(uint32_t p_conditionals, int p_fallback_mode_hint, int *r_fallback_mode, uint32_t *r_simple_fallback_version); + void _compile_simple_shader_versions(); + void iteration(); void initialize(); void finalize(); diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp index efe372cc0e83..883e583a0ef6 100644 --- a/drivers/gles3/rasterizer_storage_gles3.cpp +++ b/drivers/gles3/rasterizer_storage_gles3.cpp @@ -29,11 +29,18 @@ /*************************************************************************/ #include "rasterizer_storage_gles3.h" + #include "core/engine.h" +#include "core/os/os.h" #include "core/project_settings.h" +#include "core/threaded_callable_queue.h" #include "rasterizer_canvas_gles3.h" #include "rasterizer_scene_gles3.h" +#if defined(IPHONE_ENABLED) || defined(ANDROID_ENABLED) +#include +#endif + /* TEXTURE API */ #define _EXT_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00 @@ -2169,6 +2176,8 @@ void RasterizerStorageGLES3::_update_shader(Shader *p_shader) const { ShaderCompilerGLES3::GeneratedCode gen_code; ShaderCompilerGLES3::IdentifierActions *actions = nullptr; + int fallback_mode_hint = ShaderGLES3::FALLBACK_MODE_SIMPLE; + switch (p_shader->mode) { case VS::SHADER_CANVAS_ITEM: { p_shader->canvas_item.light_mode = Shader::CanvasItem::LIGHT_MODE_NORMAL; @@ -2249,6 +2258,10 @@ void RasterizerStorageGLES3::_update_shader(Shader *p_shader) const { shaders.actions_scene.render_mode_values["cull_back"] = Pair(&p_shader->spatial.cull_mode, Shader::Spatial::CULL_MODE_BACK); shaders.actions_scene.render_mode_values["cull_disabled"] = Pair(&p_shader->spatial.cull_mode, Shader::Spatial::CULL_MODE_DISABLED); + shaders.actions_scene.render_mode_values["fallback_simple"] = Pair(&fallback_mode_hint, ShaderGLES3::FALLBACK_MODE_SIMPLE); + shaders.actions_scene.render_mode_values["fallback_no_render"] = Pair(&fallback_mode_hint, ShaderGLES3::FALLBACK_MODE_NO_RENDER); + shaders.actions_scene.render_mode_values["fallback_none"] = Pair(&fallback_mode_hint, ShaderGLES3::FALLBACK_MODE_NONE); + shaders.actions_scene.render_mode_flags["unshaded"] = &p_shader->spatial.unshaded; shaders.actions_scene.render_mode_flags["depth_test_disable"] = &p_shader->spatial.no_depth_test; @@ -2293,7 +2306,7 @@ void RasterizerStorageGLES3::_update_shader(Shader *p_shader) const { return; } - p_shader->shader->set_custom_shader_code(p_shader->custom_code_id, gen_code.vertex, gen_code.vertex_global, gen_code.fragment, gen_code.light, gen_code.fragment_global, gen_code.uniforms, gen_code.texture_uniforms, gen_code.defines); + p_shader->shader->set_custom_shader_code(p_shader->custom_code_id, gen_code.vertex, gen_code.vertex_global, gen_code.fragment, gen_code.light, gen_code.fragment_global, gen_code.uniforms, gen_code.texture_uniforms, gen_code.defines, fallback_mode_hint); p_shader->ubo_size = gen_code.uniform_total_size; p_shader->ubo_offsets = gen_code.uniform_offsets; @@ -2509,6 +2522,16 @@ void RasterizerStorageGLES3::shader_remove_custom_define(RID p_shader, const Str _shader_make_dirty(shader); } +void RasterizerStorageGLES3::set_forced_sync_shader_compile_enabled(bool p_enabled) { + + ShaderGLES3::force_sync_compile = p_enabled; +} + +bool RasterizerStorageGLES3::is_forced_sync_shader_compile_enabled() { + + return ShaderGLES3::force_sync_compile; +} + /* COMMON MATERIAL API */ void RasterizerStorageGLES3::_material_make_dirty(Material *p_material) const { @@ -8084,8 +8107,88 @@ void RasterizerStorageGLES3::initialize() { config.anisotropic_level = MIN(int(ProjectSettings::get_singleton()->get("rendering/quality/filters/anisotropic_filter_level")), config.anisotropic_level); } +#ifdef GLES_OVER_GL + config.program_binary_supported = GLAD_GL_ARB_get_program_binary; + config.parallel_shader_compile_supported = GLAD_GL_ARB_parallel_shader_compile || GLAD_GL_KHR_parallel_shader_compile; +#else + config.program_binary_supported = true; + config.parallel_shader_compile_supported = config.extensions.has("GL_KHR_parallel_shader_compile") || config.extensions.has("GL_ARB_parallel_shader_compile"); +#endif + config.async_compilation_enabled = !Engine::get_singleton()->is_editor_hint() && (bool)ProjectSettings::get_singleton()->get("rendering/gles3/shaders/async_compile_enabled"); + config.shader_cache_enabled = (bool)ProjectSettings::get_singleton()->get("rendering/gles3/shaders/cache_enabled"); + int max_concurrent_compiles = config.async_compilation_enabled ? MAX(1, (int)ProjectSettings::get_singleton()->get("rendering/gles3/shaders/max_concurrent_compiles")) : 0; +#ifdef GLES_OVER_GL + if (GLAD_GL_ARB_parallel_shader_compile) { + glMaxShaderCompilerThreadsARB(max_concurrent_compiles); + } else if (GLAD_GL_KHR_parallel_shader_compile) { + glMaxShaderCompilerThreadsKHR(max_concurrent_compiles); + } +#else +#if defined(IPHONE_ENABLED) || defined(ANDROID_ENABLED) // TODO: Consider more platforms + void *gles2_lib = NULL; + void (*MaxShaderCompilerThreads)(GLuint) = NULL; +#if defined(IPHONE_ENABLED) + gles2_lib = dlopen(NULL, RTLD_LAZY); +#elif defined(ANDROID_ENABLED) + gles2_lib = dlopen("libGLESv2.so", RTLD_LAZY); +#endif + if (gles2_lib) { + MaxShaderCompilerThreads = (void (*)(GLuint))dlsym(gles2_lib, "glMaxShaderCompilerThreadsARB"); + if (!MaxShaderCompilerThreads) { + MaxShaderCompilerThreads = (void (*)(GLuint))dlsym(gles2_lib, "glMaxShaderCompilerThreadsKHR"); + } + } + if (MaxShaderCompilerThreads) { + MaxShaderCompilerThreads(max_concurrent_compiles); + } else { +#ifdef DEBUG_ENABLED + print_line("Async. shader compilation: No MaxShaderCompilerThreads function found."); +#endif + } +#endif +#endif + ShaderGLES3::max_shaders_compiling = MAX(1, max_concurrent_compiles); + + if (!Engine::get_singleton()->is_editor_hint()) { + ShaderGLES3::force_no_render_fallback = (bool)ProjectSettings::get_singleton()->get("rendering/gles3/shaders/force_no_render_fallback"); +#ifdef DEBUG_ENABLED + ShaderGLES3::force_use_fallbacks = (bool)ProjectSettings::get_singleton()->get("debug_force_use_fallbacks"); +#endif + } + frame.clear_request = false; + shaders.cache = nullptr; + shaders.cache_write_queue = nullptr; + if (config.shader_cache_enabled) { + if (config.program_binary_supported) { + shaders.cache = memnew(ShaderCacheGLES3); + shaders.cache_write_queue = memnew(ThreadedCallableQueue()); + print_line("Shader cache: ON"); + } else { + print_line("Shader cache: OFF (enabled for project, but not supported)"); + } + } else { + print_line("Shader cache: OFF"); + } + ShaderGLES3::shader_cache = shaders.cache; + ShaderGLES3::cache_write_queue = shaders.cache_write_queue; + + shaders.compile_queue = nullptr; + if (config.async_compilation_enabled) { + if (config.parallel_shader_compile_supported) { + print_line("Async. shader compilation: ON (full native support)"); + } else if (config.program_binary_supported && OS::get_singleton()->is_offscreen_gl_available()) { + shaders.compile_queue = memnew(ThreadedCallableQueue()); + shaders.compile_queue->enqueue(0, []() { OS::get_singleton()->set_offscreen_gl_current(true); }); + print_line("Async. shader compilation: ON (via secondary context)"); + } else { + print_line("Async. shader compilation: OFF (enabled for project, but not supported)"); + } + } else { + print_line("Async. shader compilation: OFF"); + } + shaders.copy.init(); { @@ -8300,3 +8403,17 @@ void RasterizerStorageGLES3::update_dirty_resources() { RasterizerStorageGLES3::RasterizerStorageGLES3() { config.should_orphan = true; } + +RasterizerStorageGLES3::~RasterizerStorageGLES3() { + + if (shaders.cache) { + memdelete(shaders.cache); + } + if (shaders.cache_write_queue) { + memdelete(shaders.cache_write_queue); + } + if (shaders.compile_queue) { + shaders.compile_queue->enqueue(0, []() { OS::get_singleton()->set_offscreen_gl_current(false); }); + memdelete(shaders.compile_queue); + } +} diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h index f44973dc7c1f..8592b8d475ff 100644 --- a/drivers/gles3/rasterizer_storage_gles3.h +++ b/drivers/gles3/rasterizer_storage_gles3.h @@ -35,6 +35,7 @@ #include "drivers/gles_common/rasterizer_asserts.h" #include "servers/visual/rasterizer.h" #include "servers/visual/shader_language.h" +#include "shader_cache_gles3.h" #include "shader_compiler_gles3.h" #include "shader_gles3.h" @@ -49,6 +50,8 @@ void glGetBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, GLvoid *data); #endif +template +class ThreadedCallableQueue; class RasterizerCanvasGLES3; class RasterizerSceneGLES3; @@ -112,12 +115,20 @@ class RasterizerStorageGLES3 : public RasterizerStorage { // in some cases the legacy render didn't orphan. We will mark these // so the user can switch orphaning off for them. bool should_orphan; + + bool program_binary_supported; + bool parallel_shader_compile_supported; + bool async_compilation_enabled; + bool shader_cache_enabled; } config; mutable struct Shaders { CopyShaderGLES3 copy; ShaderCompilerGLES3 compiler; + ShaderCacheGLES3 *cache; + ThreadedCallableQueue *cache_write_queue; + ThreadedCallableQueue *compile_queue; CubemapFilterShaderGLES3 cubemap_filter; @@ -546,6 +557,9 @@ class RasterizerStorageGLES3 : public RasterizerStorage { virtual void shader_get_custom_defines(RID p_shader, Vector *p_defines) const; virtual void shader_remove_custom_define(RID p_shader, const String &p_define); + virtual void set_forced_sync_shader_compile_enabled(bool p_enabled); + virtual bool is_forced_sync_shader_compile_enabled(); + void _update_shader(Shader *p_shader) const; void update_dirty_shaders(); @@ -1475,6 +1489,7 @@ class RasterizerStorageGLES3 : public RasterizerStorage { float time[4]; float delta; uint64_t count; + int shade_compiles_started; } frame; @@ -1499,6 +1514,7 @@ class RasterizerStorageGLES3 : public RasterizerStorage { bool safe_buffer_sub_data(unsigned int p_total_buffer_size, GLenum p_target, unsigned int p_offset, unsigned int p_data_size, const void *p_data, unsigned int &r_offset_after) const; RasterizerStorageGLES3(); + ~RasterizerStorageGLES3(); }; inline bool RasterizerStorageGLES3::safe_buffer_sub_data(unsigned int p_total_buffer_size, GLenum p_target, unsigned int p_offset, unsigned int p_data_size, const void *p_data, unsigned int &r_offset_after) const { diff --git a/drivers/gles3/shader_cache_gles3.cpp b/drivers/gles3/shader_cache_gles3.cpp new file mode 100644 index 000000000000..a97db1b8a79b --- /dev/null +++ b/drivers/gles3/shader_cache_gles3.cpp @@ -0,0 +1,196 @@ +/*************************************************************************/ +/* shader_cache_gles3.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "shader_cache_gles3.h" + +#include "core/crypto/crypto_core.h" +#include "core/os/dir_access.h" +#include "core/os/os.h" +#include "core/sort_array.h" +#include "core/ustring.h" + +const char *const ShaderCacheGLES3::STORAGE_SUBPATH = "godot_shaders/"; +const uint64_t ShaderCacheGLES3::MAX_STORAGE_SIZE = 512 * 1024 * 1024; + +String ShaderCacheGLES3::hash_program(const char *const *p_strings_platform, const LocalVector &p_vertex_strings, const LocalVector &p_fragment_strings) { + CryptoCore::SHA256Context ctx; + ctx.start(); + + // GL may already reject a binary program if harware/software has changed, but just in case + for (const char *const *s = p_strings_platform; *s; s++) { + uint8_t *bytes = reinterpret_cast(const_cast(*s)); + ctx.update(bytes, strlen(*s)); + } + for (uint32_t i = 0; i < p_vertex_strings.size(); i++) { + ctx.update((uint8_t *)p_vertex_strings[i], strlen(p_vertex_strings[i])); + } + for (uint32_t i = 0; i < p_fragment_strings.size(); i++) { + ctx.update((uint8_t *)p_fragment_strings[i], strlen(p_fragment_strings[i])); + } + + uint8_t hash[32]; + ctx.finish(hash); + return String::hex_encode_buffer(hash, 32); +} + +bool ShaderCacheGLES3::retrieve(const String &p_program_hash, uint32_t *r_format, PoolByteArray *r_data) { + if (!storage_da) { + return false; + } + + FileAccessRef fa = FileAccess::open(storage_path.plus_file(p_program_hash), FileAccess::READ_WRITE); + if (!fa) { + return false; + } + + *r_format = fa->get_32(); + uint32_t binary_len = fa->get_32(); + if (binary_len <= 0 || binary_len > 0x10000000) { + ERR_PRINT("Program binary cache file is corrupted. Ignoring and removing."); + fa->close(); + storage_da->remove(p_program_hash); + return false; + } + r_data->resize(binary_len); + PoolByteArray::Write w = r_data->write(); + if (fa->get_buffer(w.ptr(), binary_len) != static_cast(binary_len)) { + ERR_PRINT("Program binary cache file is truncated. Ignoring and removing."); + fa->close(); + storage_da->remove(p_program_hash); + return false; + } + + // Force update modification time (for LRU purge) + fa->seek(0); + fa->store_32(*r_format); + + return true; +} + +void ShaderCacheGLES3::store(const String &p_program_hash, uint32_t p_program_format, const PoolByteArray &p_program_data) { + if (!storage_da) { + return; + } + + FileAccessRef fa = FileAccess::open(storage_path.plus_file(p_program_hash), FileAccess::WRITE); + ERR_FAIL_COND(!fa); + fa->store_32(p_program_format); + fa->store_32(p_program_data.size()); + PoolByteArray::Read r = p_program_data.read(); + fa->store_buffer(r.ptr(), p_program_data.size()); +} + +void ShaderCacheGLES3::remove(const String &p_program_hash) { + if (!storage_da) { + return; + } + + storage_da->remove(p_program_hash); +} + +void ShaderCacheGLES3::_purge_excess() { + if (!storage_da) { + return; + } + + struct Entry { + String name; + uint64_t timestamp; + uint64_t size; + + bool operator<(const Entry &p_rhs) const { + return timestamp < p_rhs.timestamp; + } + }; + LocalVector entries; + uint64_t total_size = 0; + + ERR_FAIL_COND(storage_da->list_dir_begin() != OK); + while (true) { + String f = storage_da->get_next(); + if (f == "") { + break; + } + if (storage_da->current_is_dir()) { + continue; + } + String path = storage_da->get_current_dir().plus_file(f); + FileAccessRef fa = FileAccess::open(path, FileAccess::READ); + ERR_CONTINUE(!fa); + + Entry entry; + entry.name = f; + entry.timestamp = FileAccess::get_modified_time(path); + entry.size = fa->get_len(); + entries.push_back(entry); + total_size += entry.size; + } + storage_da->list_dir_end(); + + print_line("Shader cache size: " + itos(total_size / (1024 * 1024)) + " MiB"); + if (total_size > MAX_STORAGE_SIZE) { + print_line("Purging LRU from shader cache."); + SortArray().sort(entries.ptr(), entries.size()); + for (uint32_t i = 0; i < entries.size(); i++) { + storage_da->remove(entries[i].name); + total_size -= entries[i].size; + if (total_size <= MAX_STORAGE_SIZE) { + break; + } + } + } +} + +ShaderCacheGLES3::ShaderCacheGLES3() { + storage_da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + storage_path = OS::get_singleton()->get_cache_path().plus_file(STORAGE_SUBPATH); + + print_line("Shader cache path: " + storage_path); + if (storage_da->make_dir_recursive(storage_path) != OK) { + ERR_PRINT("Couldn't create shader cache directory. Shader cache disabled."); + memdelete(storage_da); + storage_da = nullptr; + return; + } + if (storage_da->change_dir(storage_path) != OK) { + ERR_PRINT("Couldn't open shader cache directory. Shader cache disabled."); + memdelete(storage_da); + storage_da = nullptr; + return; + } + + _purge_excess(); +} + +ShaderCacheGLES3::~ShaderCacheGLES3() { + if (storage_da) { + memdelete(storage_da); + } +} diff --git a/drivers/gles3/shader_cache_gles3.h b/drivers/gles3/shader_cache_gles3.h new file mode 100644 index 000000000000..0e6c71de8224 --- /dev/null +++ b/drivers/gles3/shader_cache_gles3.h @@ -0,0 +1,60 @@ +/*************************************************************************/ +/* shader_cache_gles3.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SHADER_CACHE_GLES3_H +#define SHADER_CACHE_GLES3_H + +#include "core/local_vector.h" +#include "core/reference.h" + +class DirAccess; +class String; + +class ShaderCacheGLES3 { + static const char *const STORAGE_SUBPATH; + static const uint64_t MAX_STORAGE_SIZE; + + DirAccess *storage_da; + String storage_path; + + void _purge_excess(); + +public: + static String hash_program(const char *const *p_platform_strings, const LocalVector &p_vertex_strings, const LocalVector &p_fragment_strings); + + bool retrieve(const String &p_program_hash, uint32_t *r_format, PoolByteArray *r_data); + void store(const String &p_program_hash, uint32_t p_program_format, const PoolByteArray &p_program_data); + void remove(const String &p_program_hash); + + ShaderCacheGLES3(); + ~ShaderCacheGLES3(); +}; + +#endif diff --git a/drivers/gles3/shader_gles3.cpp b/drivers/gles3/shader_gles3.cpp index af03b0b6a02a..47157ac4fc5e 100644 --- a/drivers/gles3/shader_gles3.cpp +++ b/drivers/gles3/shader_gles3.cpp @@ -30,7 +30,11 @@ #include "shader_gles3.h" +#include "core/local_vector.h" +#include "core/os/os.h" #include "core/print_string.h" +#include "core/threaded_callable_queue.h" +#include "drivers/gles3/shader_cache_gles3.h" //#define DEBUG_OPENGL @@ -50,6 +54,20 @@ #endif ShaderGLES3 *ShaderGLES3::active = nullptr; +SelfList::List ShaderGLES3::versions_compiling; + +ShaderCacheGLES3 *ShaderGLES3::shader_cache; +ThreadedCallableQueue *ShaderGLES3::cache_write_queue; + +bool ShaderGLES3::force_no_render_fallback; +#ifdef DEBUG_ENABLED +bool ShaderGLES3::force_use_fallbacks; +#endif +bool ShaderGLES3::force_sync_compile; +int *ShaderGLES3::shader_compiles_started_this_frame; +int ShaderGLES3::max_shaders_compiling; +int ShaderGLES3::versions_compiling_count; +uint64_t ShaderGLES3::current_frame; //#define DEBUG_SHADER @@ -63,6 +81,8 @@ ShaderGLES3 *ShaderGLES3::active = nullptr; #endif +#define _EXT_COMPLETION_STATUS 0x91B1 + void ShaderGLES3::bind_uniforms() { if (!uniforms_dirty) { return; @@ -107,7 +127,16 @@ GLint ShaderGLES3::get_uniform_location(int p_index) const { return version->uniform_location[p_index]; } -bool ShaderGLES3::bind() { +bool ShaderGLES3::bind(bool *r_fallen_back) { + return _bind(false, r_fallen_back); +} + +// Returning true and setting r_fallen_back to true means the simple shader kicks in. +bool ShaderGLES3::_bind(bool p_fallback_forbidden, bool *r_fallen_back) { + if (r_fallen_back) { + *r_fallen_back = false; + } + if (active != this || !version || new_conditional_version.key != conditional_version.key) { conditional_version = new_conditional_version; version = get_current_version(); @@ -117,18 +146,223 @@ bool ShaderGLES3::bind() { ERR_FAIL_COND_V(!version, false); - if (!version->ok) { //broken, unable to bind (do not throw error, you saw it before already when it failed compilation). - glUseProgram(0); - return false; + bool ready = false; +#ifdef DEBUG_ENABLED + bool force_fallback = force_use_fallbacks; +#else + bool force_fallback = false; +#endif + force_fallback = force_fallback && !p_fallback_forbidden && version->fallback_mode != FALLBACK_MODE_NONE; + if (!force_fallback) { + version->last_frame_processed = current_frame; + ready = _process_program_state(); + if (version->compile_status == Version::COMPILE_STATUS_RESTART_NEEDED) { + get_current_version(); // Trigger recompile + ready = _process_program_state(); + } } - glUseProgram(version->id); + if (ready) { + glUseProgram(version->ids.main); + if (!version->uniforms_ready) { + _setup_uniforms(custom_code_map.getptr(conditional_version.code_version)); + version->uniforms_ready = true; + } + DEBUG_TEST_ERROR("Use Program"); + active = this; + uniforms_dirty = true; + return true; + } else { + if (r_fallen_back) { + *r_fallen_back = true; + } + if (version->fallback_mode == FALLBACK_MODE_SIMPLE && !p_fallback_forbidden) { + // We can fall back to the simple shader + new_conditional_version.version = version->simple_fallback_version; + new_conditional_version.code_version = fallback_code_id; + return _bind(true, nullptr); + } else { + // We have a compile error or must fall back by skipping render + // or this is a simple fallback shader not being ready yet + unbind(); + return false; + } + } +} - DEBUG_TEST_ERROR("Use Program"); +void ShaderGLES3::advance_async_shaders_compilation() { + SelfList *curr = versions_compiling.first(); + while (curr) { + SelfList *next = curr->next(); - active = this; - uniforms_dirty = true; - return true; + ShaderGLES3::Version *v = curr->self(); + // Only if it didn't already have a chance to be processed in this frame + if (v->last_frame_processed != current_frame) { + v->shader->new_conditional_version.key = v->version_key; + v->shader->_bind(true); + } + + curr = next; + } +} + +bool ShaderGLES3::_process_program_state() { + bool ready = false; + bool run_next_step = true; + while (run_next_step) { + run_next_step = false; + switch (version->compile_status) { + case Version::COMPILE_STATUS_OK: { + // Yeaaah! + ready = true; + } break; + case Version::COMPILE_STATUS_ERROR: { + // Sad, but we have to accept it + } break; + case Version::COMPILE_STATUS_PENDING: + case Version::COMPILE_STATUS_RESTART_NEEDED: { + // These lead to nowhere unless other piece of code starts the compile process + } break; + case Version::COMPILE_STATUS_SOURCE_PROVIDED: { + bool compile_now = version->fallback_mode == FALLBACK_MODE_NONE || force_sync_compile; + if (!compile_now) { + if (versions_compiling_count < max_shaders_compiling && *shader_compiles_started_this_frame < max_shaders_compiling) { + compile_now = true; + } + } + if (compile_now) { + _compile(version->ids); + version->compile_status = Version::COMPILE_STATUS_COMPILING; + versions_compiling.add_last(&version->compiling_list); + versions_compiling_count++; + (*shader_compiles_started_this_frame)++; + run_next_step = true; + } + } break; + case Version::COMPILE_STATUS_COMPILING: { + bool must_complete_now = version->fallback_mode == FALLBACK_MODE_NONE || force_sync_compile; + if (!must_complete_now && parallel_compile_supported) { + GLint compile_completed; + glGetShaderiv(version->ids.vert, _EXT_COMPLETION_STATUS, &compile_completed); + if (compile_completed) { + glGetShaderiv(version->ids.frag, _EXT_COMPLETION_STATUS, &compile_completed); + must_complete_now = compile_completed; + } + } + if (must_complete_now) { + bool must_provide_binary = version->program_binary.source != Version::ProgramBinary::SOURCE_CACHE && shader_cache; + bool ok = _complete_compile(version->ids, must_provide_binary); + if (ok) { + version->compile_status = Version::COMPILE_STATUS_LINKING; + run_next_step = true; + } else { + version->compile_status = Version::COMPILE_STATUS_ERROR; + version->compiling_list.remove_from_list(); + versions_compiling_count--; + ERR_CONTINUE(versions_compiling_count < 0); + } + } + } break; + case Version::COMPILE_STATUS_PROCESSING_AT_QUEUE: { + // This is from the async. queue + switch (version->program_binary.result_from_queue.get()) { + case -1: { // Error + version->compile_status = Version::COMPILE_STATUS_ERROR; + version->compiling_list.remove_from_list(); + versions_compiling_count--; + ERR_CONTINUE(versions_compiling_count < 0); + } break; + case 0: { // In progress + if (force_sync_compile) { + OS::get_singleton()->delay_usec(1000); + run_next_step = true; + } + } break; + case 1: { // Complete + version->compile_status = Version::COMPILE_STATUS_BINARY_READY; + run_next_step = true; + } break; + } + } break; + case Version::COMPILE_STATUS_BINARY_READY_FROM_CACHE: { + bool eat_binary_now = version->fallback_mode == FALLBACK_MODE_NONE || force_sync_compile; + if (!eat_binary_now) { + if (versions_compiling_count < max_shaders_compiling && *shader_compiles_started_this_frame < max_shaders_compiling) { + eat_binary_now = true; + } + } + if (eat_binary_now) { + version->compile_status = Version::COMPILE_STATUS_BINARY_READY; + run_next_step = true; + versions_compiling.add_last(&version->compiling_list); + versions_compiling_count++; + (*shader_compiles_started_this_frame)++; + } + } break; + case Version::COMPILE_STATUS_BINARY_READY: { + PoolByteArray::Read r = version->program_binary.data.read(); + glProgramBinary(version->ids.main, static_cast(version->program_binary.format), r.ptr(), version->program_binary.data.size()); + version->compile_status = Version::COMPILE_STATUS_LINKING; + run_next_step = true; + } break; + case Version::COMPILE_STATUS_LINKING: { + bool must_complete_now = version->fallback_mode == FALLBACK_MODE_NONE || force_sync_compile; + if (!must_complete_now && parallel_compile_supported) { + GLint link_completed; + glGetProgramiv(version->ids.main, _EXT_COMPLETION_STATUS, &link_completed); + must_complete_now = link_completed; + } + if (must_complete_now) { + bool must_save_to_cache = version->program_binary.source != Version::ProgramBinary::SOURCE_CACHE && shader_cache; + bool ok; + if (must_save_to_cache && version->program_binary.source == Version::ProgramBinary::SOURCE_LOCAL) { + ok = _complete_link(version->ids, &version->program_binary.format, &version->program_binary.data); + } else { + ok = _complete_link(version->ids); +#ifdef DEBUG_ENABLED +#if 0 + // Simulate GL rejecting program from cache + if (version->program_binary.source == Version::ProgramBinary::SOURCE_CACHE) { + ok = false; + } +#endif +#endif + } + if (ok) { + if (must_save_to_cache) { + String &tmp_hash = version->program_binary.cache_hash; + GLenum &tmp_format = version->program_binary.format; + PoolByteArray &tmp_data = version->program_binary.data; + cache_write_queue->enqueue(version->ids.main, [=]() { + shader_cache->store(tmp_hash, static_cast(tmp_format), tmp_data); + }); + } + version->compile_status = Version::COMPILE_STATUS_OK; + ready = true; + } else { + if (version->program_binary.source == Version::ProgramBinary::SOURCE_CACHE) { +#ifdef DEBUG_ENABLED + WARN_PRINT("Program binary from cache has been rejected by the GL. Removing."); +#endif + shader_cache->remove(version->program_binary.cache_hash); + version->compile_status = Version::COMPILE_STATUS_RESTART_NEEDED; + } else { + if (version->program_binary.source == Version::ProgramBinary::SOURCE_QUEUE) { + ERR_PRINT("Program binary from compile queue has been rejected by the GL. Bug?"); + } + version->compile_status = Version::COMPILE_STATUS_ERROR; + } + } + version->program_binary.data = PoolByteArray(); + version->program_binary.cache_hash.clear(); + version->compiling_list.remove_from_list(); + versions_compiling_count--; + ERR_CONTINUE(versions_compiling_count < 0); + } + } break; + } + } + return ready; } void ShaderGLES3::unbind() { @@ -138,14 +372,16 @@ void ShaderGLES3::unbind() { active = nullptr; } -static void _display_error_with_code(const String &p_error, const Vector &p_code) { +static void _display_error_with_code(const String &p_error, GLuint p_shader_id) { int line = 1; - String total_code; - for (int i = 0; i < p_code.size(); i++) { - total_code += String(p_code[i]); - } + GLint source_len; + glGetShaderiv(p_shader_id, GL_SHADER_SOURCE_LENGTH, &source_len); + LocalVector source_buffer; + source_buffer.resize(source_len); + glGetShaderSource(p_shader_id, source_len, NULL, source_buffer.ptr()); + String total_code(source_buffer.ptr()); Vector lines = String(total_code).split("\n"); for (int j = 0; j < lines.size(); j++) { @@ -156,63 +392,67 @@ static void _display_error_with_code(const String &p_error, const Vectorversion == _v->code_version) { + if (_v->compile_status == Version::COMPILE_STATUS_RESTART_NEEDED) { + _v->program_binary.source = Version::ProgramBinary::SOURCE_NONE; + } else { + CustomCode *cc = nullptr; + if (conditional_version.code_version != 0) { + cc = custom_code_map.getptr(conditional_version.code_version); + ERR_FAIL_COND_V(!cc, _v); + if (cc->version == _v->code_version) { + return _v; + } else { + new_code_version = true; + } + } else { return _v; } - } else { - return _v; } } if (!_v) { - version_map[conditional_version] = Version(); + _v = &version_map[conditional_version]; + _v->shader = this; + _v->version_key = conditional_version.key; + _v->uniform_location = memnew_arr(GLint, uniform_count); + } else if (new_code_version) { + _dispose_program(_v); + _v->uniforms_ready = false; } - Version &v = version_map[conditional_version]; - - if (!_v) { - v.uniform_location = memnew_arr(GLint, uniform_count); - - } else { - if (v.ok) { - //bye bye shaders - glDeleteShader(v.vert_id); - glDeleteShader(v.frag_id); - glDeleteProgram(v.id); - v.id = 0; - } - } + Version &v = *_v; - v.ok = false; /* SETUP CONDITIONALS */ - Vector strings; + LocalVector strings_common; #ifdef GLES_OVER_GL - strings.push_back("#version 330\n"); - strings.push_back("#define GLES_OVER_GL\n"); + strings_common.push_back("#version 330\n"); + strings_common.push_back("#define GLES_OVER_GL\n"); #else - strings.push_back("#version 300 es\n"); + strings_common.push_back("#version 300 es\n"); #endif #ifdef ANDROID_ENABLED - strings.push_back("#define ANDROID_ENABLED\n"); + strings_common.push_back("#define ANDROID_ENABLED\n"); #endif for (int i = 0; i < custom_defines.size(); i++) { - strings.push_back(custom_defines[i].get_data()); - strings.push_back("\n"); + strings_common.push_back(custom_defines[i].get_data()); + strings_common.push_back("\n"); } for (int j = 0; j < conditional_count; j++) { bool enable = ((1 << j) & conditional_version.version); - strings.push_back(enable ? conditional_defines[j] : ""); + strings_common.push_back(enable ? conditional_defines[j] : ""); if (enable) { DEBUG_PRINT(conditional_defines[j]); @@ -220,10 +460,17 @@ ShaderGLES3::Version *ShaderGLES3::get_current_version() { } //keep them around during the function - CharString code_string; - CharString code_string2; - CharString code_globals; - CharString material_string; + struct { + CharString code_string; + CharString code_globals; + CharString material_string; + } vert; + struct { + CharString code_string; + CharString code_string2; + CharString code_globals; + CharString material_string; + } frag; CustomCode *cc = nullptr; @@ -237,166 +484,262 @@ ShaderGLES3::Version *ShaderGLES3::get_current_version() { /* CREATE PROGRAM */ - v.id = glCreateProgram(); + v.ids.main = glCreateProgram(); - ERR_FAIL_COND_V(v.id == 0, nullptr); + ERR_FAIL_COND_V(v.ids.main == 0, nullptr); /* VERTEX SHADER */ if (cc) { for (int i = 0; i < cc->custom_defines.size(); i++) { - strings.push_back(cc->custom_defines[i].get_data()); + strings_common.push_back(cc->custom_defines[i].get_data()); DEBUG_PRINT("CD #" + itos(i) + ": " + String(cc->custom_defines[i])); } } - int strings_base_size = strings.size(); + LocalVector strings_vertex(strings_common); //vertex precision is high - strings.push_back("precision highp float;\n"); - strings.push_back("precision highp int;\n"); + strings_vertex.push_back("precision highp float;\n"); + strings_vertex.push_back("precision highp int;\n"); #ifndef GLES_OVER_GL - strings.push_back("precision highp sampler2D;\n"); - strings.push_back("precision highp samplerCube;\n"); - strings.push_back("precision highp sampler2DArray;\n"); + strings_vertex.push_back("precision highp sampler2D;\n"); + strings_vertex.push_back("precision highp samplerCube;\n"); + strings_vertex.push_back("precision highp sampler2DArray;\n"); #endif - strings.push_back(vertex_code0.get_data()); + strings_vertex.push_back(vertex_code0.get_data()); if (cc) { - material_string = cc->uniforms.ascii(); - strings.push_back(material_string.get_data()); + vert.material_string = cc->uniforms.ascii(); + strings_vertex.push_back(vert.material_string.get_data()); } - strings.push_back(vertex_code1.get_data()); + strings_vertex.push_back(vertex_code1.get_data()); if (cc) { - code_globals = cc->vertex_globals.ascii(); - strings.push_back(code_globals.get_data()); + vert.code_globals = cc->vertex_globals.ascii(); + strings_vertex.push_back(vert.code_globals.get_data()); } - strings.push_back(vertex_code2.get_data()); + strings_vertex.push_back(vertex_code2.get_data()); if (cc) { - code_string = cc->vertex.ascii(); - strings.push_back(code_string.get_data()); + vert.code_string = cc->vertex.ascii(); + strings_vertex.push_back(vert.code_string.get_data()); } - strings.push_back(vertex_code3.get_data()); -#ifdef DEBUG_SHADER + strings_vertex.push_back(vertex_code3.get_data()); +#ifdef DEBUG_SHADER DEBUG_PRINT("\nVertex Code:\n\n" + String(code_string.get_data())); - for (int i = 0; i < strings.size(); i++) { - //print_line("vert strings "+itos(i)+":"+String(strings[i])); + for (int i = 0; i < strings_vertex.size(); i++) { + //print_line("vert strings "+itos(i)+":"+String(strings_vertex[i])); } #endif - v.vert_id = glCreateShader(GL_VERTEX_SHADER); - glShaderSource(v.vert_id, strings.size(), &strings[0], nullptr); - glCompileShader(v.vert_id); - - GLint status; - - glGetShaderiv(v.vert_id, GL_COMPILE_STATUS, &status); - if (status == GL_FALSE) { - // error compiling - GLsizei iloglen; - glGetShaderiv(v.vert_id, GL_INFO_LOG_LENGTH, &iloglen); - - if (iloglen < 0) { - glDeleteShader(v.vert_id); - glDeleteProgram(v.id); - v.id = 0; - - ERR_PRINT("Vertex shader compilation failed with empty log"); - } else { - if (iloglen == 0) { - iloglen = 4096; //buggy driver (Adreno 220+....) - } - - char *ilogmem = (char *)memalloc(iloglen + 1); - ilogmem[iloglen] = 0; - glGetShaderInfoLog(v.vert_id, iloglen, &iloglen, ilogmem); - - String err_string = get_shader_name() + ": Vertex Program Compilation Failed:\n"; - - err_string += ilogmem; - _display_error_with_code(err_string, strings); - memfree(ilogmem); - glDeleteShader(v.vert_id); - glDeleteProgram(v.id); - v.id = 0; - } - - ERR_FAIL_V(nullptr); - } - - //_display_error_with_code("pepo", strings); - /* FRAGMENT SHADER */ - strings.resize(strings_base_size); + LocalVector strings_fragment(strings_common); + //fragment precision is medium - strings.push_back("precision highp float;\n"); - strings.push_back("precision highp int;\n"); + strings_fragment.push_back("precision highp float;\n"); + strings_fragment.push_back("precision highp int;\n"); #ifndef GLES_OVER_GL - strings.push_back("precision highp sampler2D;\n"); - strings.push_back("precision highp samplerCube;\n"); - strings.push_back("precision highp sampler2DArray;\n"); + strings_fragment.push_back("precision highp sampler2D;\n"); + strings_fragment.push_back("precision highp samplerCube;\n"); + strings_fragment.push_back("precision highp sampler2DArray;\n"); #endif - strings.push_back(fragment_code0.get_data()); + strings_fragment.push_back(fragment_code0.get_data()); if (cc) { - material_string = cc->uniforms.ascii(); - strings.push_back(material_string.get_data()); + frag.material_string = cc->uniforms.ascii(); + strings_fragment.push_back(frag.material_string.get_data()); } - strings.push_back(fragment_code1.get_data()); + strings_fragment.push_back(fragment_code1.get_data()); if (cc) { - code_globals = cc->fragment_globals.ascii(); - strings.push_back(code_globals.get_data()); + frag.code_globals = cc->fragment_globals.ascii(); + strings_fragment.push_back(frag.code_globals.get_data()); } - strings.push_back(fragment_code2.get_data()); + strings_fragment.push_back(fragment_code2.get_data()); if (cc) { - code_string = cc->light.ascii(); - strings.push_back(code_string.get_data()); + frag.code_string = cc->light.ascii(); + strings_fragment.push_back(frag.code_string.get_data()); } - strings.push_back(fragment_code3.get_data()); + strings_fragment.push_back(fragment_code3.get_data()); if (cc) { - code_string2 = cc->fragment.ascii(); - strings.push_back(code_string2.get_data()); + frag.code_string2 = cc->fragment.ascii(); + strings_fragment.push_back(frag.code_string2.get_data()); } - strings.push_back(fragment_code4.get_data()); + strings_fragment.push_back(fragment_code4.get_data()); #ifdef DEBUG_SHADER DEBUG_PRINT("\nFragment Globals:\n\n" + String(code_globals.get_data())); DEBUG_PRINT("\nFragment Code:\n\n" + String(code_string2.get_data())); - for (int i = 0; i < strings.size(); i++) { - //print_line("frag strings "+itos(i)+":"+String(strings[i])); + for (int i = 0; i < strings_fragment.size(); i++) { + //print_line("frag strings "+itos(i)+":"+String(strings_fragment[i])); } #endif - v.frag_id = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(v.frag_id, strings.size(), &strings[0], nullptr); - glCompileShader(v.frag_id); + if (decide_fallback_mode_func) { + if (force_no_render_fallback) { + v.fallback_mode = FALLBACK_MODE_NO_RENDER; + } else { + decide_fallback_mode_func(conditional_version.version, cc->fallback_mode_hint, &v.fallback_mode, &v.simple_fallback_version); + } + } + + bool in_cache = false; + if (shader_cache) { + const char *strings_platform[] = { + reinterpret_cast(glGetString(GL_VENDOR)), + reinterpret_cast(glGetString(GL_RENDERER)), + reinterpret_cast(glGetString(GL_VERSION)), + nullptr, + }; + v.program_binary.cache_hash = ShaderCacheGLES3::hash_program(strings_platform, strings_vertex, strings_fragment); + if (shader_cache->retrieve(v.program_binary.cache_hash, &v.program_binary.format, &v.program_binary.data)) { + in_cache = true; + v.program_binary.source = Version::ProgramBinary::SOURCE_CACHE; + v.compile_status = Version::COMPILE_STATUS_BINARY_READY_FROM_CACHE; + } + } + if (!in_cache) { + if (v.fallback_mode != FALLBACK_MODE_NONE && compile_queue) { + // Asynchronous compilation via queue (secondary context) + // Remarks: + // 1. We need to save vertex and fragment strings because they will not live beyond this function. + // 2. We'll create another program since the other GL context is not shared. + // We are doing it that way since GL drivers can implement context sharing via locking, which + // would render (no pun intended) this whole effort to asynchronous useless. + + auto concat_shader_strings = [](const LocalVector &p_shader_strings, LocalVector *r_out) { + r_out->clear(); + for (uint32_t i = 0; i < p_shader_strings.size(); i++) { + uint32_t initial_size = r_out->size(); + uint32_t piece_len = strlen(reinterpret_cast(p_shader_strings[i])); + r_out->resize(initial_size + piece_len + 1); + memcpy(r_out->ptr() + initial_size, p_shader_strings[i], piece_len); + *(r_out->ptr() + initial_size + piece_len) = '\n'; + } + *(r_out->ptr() + r_out->size() - 1) = '\0'; + }; + + LocalVector vertex_code; + concat_shader_strings(strings_vertex, &vertex_code); + LocalVector fragment_code; + concat_shader_strings(strings_fragment, &fragment_code); + + v.program_binary.source = Version::ProgramBinary::SOURCE_QUEUE; + v.compile_status = Version::COMPILE_STATUS_PROCESSING_AT_QUEUE; + versions_compiling.add_last(&v.compiling_list); + versions_compiling_count++; + (*shader_compiles_started_this_frame)++; + + compile_queue->enqueue(v.ids.main, [this, &v, vertex_code, fragment_code]() { + Version::Ids async_ids; + async_ids.main = glCreateProgram(); + async_ids.vert = glCreateShader(GL_VERTEX_SHADER); + async_ids.frag = glCreateShader(GL_FRAGMENT_SHADER); + + LocalVector async_strings_vertex; + async_strings_vertex.push_back(vertex_code.ptr()); + LocalVector async_strings_fragment; + async_strings_fragment.push_back(fragment_code.ptr()); + + _set_source(async_ids, async_strings_vertex, async_strings_fragment); + _compile(async_ids); + if (_complete_compile(async_ids, true) && _complete_link(async_ids, &v.program_binary.format, &v.program_binary.data)) { + glDeleteShader(async_ids.frag); + glDeleteShader(async_ids.vert); + glDeleteProgram(async_ids.main); + v.program_binary.result_from_queue.set(1); + } else { + v.program_binary.result_from_queue.set(0); + } + }); + } else { + // Synchronous compilation, or async. via native support + v.ids.vert = glCreateShader(GL_VERTEX_SHADER); + v.ids.frag = glCreateShader(GL_FRAGMENT_SHADER); + _set_source(v.ids, strings_vertex, strings_fragment); + v.program_binary.source = Version::ProgramBinary::SOURCE_LOCAL; + v.compile_status = Version::COMPILE_STATUS_SOURCE_PROVIDED; + } + } + + if (cc) { + cc->versions.insert(conditional_version.version); + } + + return &v; +} + +void ShaderGLES3::_set_source(Version::Ids p_ids, const LocalVector &p_vertex_strings, const LocalVector &p_fragment_strings) const { + glShaderSource(p_ids.vert, p_vertex_strings.size(), p_vertex_strings.ptr(), NULL); + glShaderSource(p_ids.frag, p_fragment_strings.size(), p_fragment_strings.ptr(), NULL); +} + +void ShaderGLES3::_compile(Version::Ids p_ids) const { + glCompileShader(p_ids.vert); + glCompileShader(p_ids.frag); +} + +bool ShaderGLES3::_complete_compile(Version::Ids p_ids, bool p_retrievable) const { + GLint status; + + glGetShaderiv(p_ids.vert, GL_COMPILE_STATUS, &status); + if (status == GL_FALSE) { + // error compiling + GLsizei iloglen; + glGetShaderiv(p_ids.vert, GL_INFO_LOG_LENGTH, &iloglen); + + if (iloglen < 0) { + glDeleteShader(p_ids.frag); + glDeleteShader(p_ids.vert); + glDeleteProgram(p_ids.main); + + ERR_PRINT("Vertex shader compilation failed with empty log"); + } else { + if (iloglen == 0) { + iloglen = 4096; //buggy driver (Adreno 220+....) + } - glGetShaderiv(v.frag_id, GL_COMPILE_STATUS, &status); + char *ilogmem = (char *)memalloc(iloglen + 1); + ilogmem[iloglen] = 0; + glGetShaderInfoLog(p_ids.vert, iloglen, &iloglen, ilogmem); + + String err_string = get_shader_name() + ": Vertex Program Compilation Failed:\n"; + + err_string += ilogmem; + _display_error_with_code(err_string, p_ids.vert); + ERR_PRINT(err_string.ascii().get_data()); + memfree(ilogmem); + glDeleteShader(p_ids.frag); + glDeleteShader(p_ids.vert); + glDeleteProgram(p_ids.main); + } + + return false; + } + + glGetShaderiv(p_ids.frag, GL_COMPILE_STATUS, &status); if (status == GL_FALSE) { // error compiling GLsizei iloglen; - glGetShaderiv(v.frag_id, GL_INFO_LOG_LENGTH, &iloglen); + glGetShaderiv(p_ids.frag, GL_INFO_LOG_LENGTH, &iloglen); if (iloglen < 0) { - glDeleteShader(v.frag_id); - glDeleteShader(v.vert_id); - glDeleteProgram(v.id); - v.id = 0; + glDeleteShader(p_ids.frag); + glDeleteShader(p_ids.vert); + glDeleteProgram(p_ids.main); ERR_PRINT("Fragment shader compilation failed with empty log"); } else { if (iloglen == 0) { @@ -405,29 +748,28 @@ ShaderGLES3::Version *ShaderGLES3::get_current_version() { char *ilogmem = (char *)memalloc(iloglen + 1); ilogmem[iloglen] = 0; - glGetShaderInfoLog(v.frag_id, iloglen, &iloglen, ilogmem); + glGetShaderInfoLog(p_ids.frag, iloglen, &iloglen, ilogmem); String err_string = get_shader_name() + ": Fragment Program Compilation Failed:\n"; err_string += ilogmem; - _display_error_with_code(err_string, strings); + _display_error_with_code(err_string, p_ids.frag); ERR_PRINT(err_string.ascii().get_data()); memfree(ilogmem); - glDeleteShader(v.frag_id); - glDeleteShader(v.vert_id); - glDeleteProgram(v.id); - v.id = 0; + glDeleteShader(p_ids.frag); + glDeleteShader(p_ids.vert); + glDeleteProgram(p_ids.main); } - ERR_FAIL_V(nullptr); + return false; } - glAttachShader(v.id, v.frag_id); - glAttachShader(v.id, v.vert_id); + glAttachShader(p_ids.main, p_ids.frag); + glAttachShader(p_ids.main, p_ids.vert); // bind attributes before linking for (int i = 0; i < attribute_pair_count; i++) { - glBindAttribLocation(v.id, attribute_pairs[i].index, attribute_pairs[i].name); + glBindAttribLocation(p_ids.main, attribute_pairs[i].index, attribute_pairs[i].name); } //if feedback exists, set it up @@ -442,25 +784,32 @@ ShaderGLES3::Version *ShaderGLES3::get_current_version() { } if (feedback.size()) { - glTransformFeedbackVaryings(v.id, feedback.size(), feedback.ptr(), GL_INTERLEAVED_ATTRIBS); + glTransformFeedbackVaryings(p_ids.main, feedback.size(), feedback.ptr(), GL_INTERLEAVED_ATTRIBS); } } - glLinkProgram(v.id); + if (p_retrievable) { + glProgramParameteri(p_ids.main, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE); + } + glLinkProgram(p_ids.main); - glGetProgramiv(v.id, GL_LINK_STATUS, &status); + return true; +} + +bool ShaderGLES3::_complete_link(Version::Ids p_ids, GLenum *r_program_format, PoolByteArray *r_program_binary) const { + GLint status; + glGetProgramiv(p_ids.main, GL_LINK_STATUS, &status); if (status == GL_FALSE) { // error linking GLsizei iloglen; - glGetProgramiv(v.id, GL_INFO_LOG_LENGTH, &iloglen); + glGetProgramiv(p_ids.main, GL_INFO_LOG_LENGTH, &iloglen); if (iloglen < 0) { - glDeleteShader(v.frag_id); - glDeleteShader(v.vert_id); - glDeleteProgram(v.id); - v.id = 0; - ERR_FAIL_COND_V(iloglen < 0, nullptr); + glDeleteShader(p_ids.frag); + glDeleteShader(p_ids.vert); + glDeleteProgram(p_ids.main); + ERR_FAIL_COND_V(iloglen < 0, false); } if (iloglen == 0) { @@ -469,35 +818,41 @@ ShaderGLES3::Version *ShaderGLES3::get_current_version() { char *ilogmem = (char *)Memory::alloc_static(iloglen + 1); ilogmem[iloglen] = 0; - glGetProgramInfoLog(v.id, iloglen, &iloglen, ilogmem); + glGetProgramInfoLog(p_ids.main, iloglen, &iloglen, ilogmem); String err_string = get_shader_name() + ": Program LINK FAILED:\n"; err_string += ilogmem; - _display_error_with_code(err_string, strings); ERR_PRINT(err_string.ascii().get_data()); Memory::free_static(ilogmem); - glDeleteShader(v.frag_id); - glDeleteShader(v.vert_id); - glDeleteProgram(v.id); - v.id = 0; + glDeleteShader(p_ids.frag); + glDeleteShader(p_ids.vert); + glDeleteProgram(p_ids.main); - ERR_FAIL_V(nullptr); + return false; } - /* UNIFORMS */ + if (r_program_binary) { + GLint program_len; + glGetProgramiv(p_ids.main, GL_PROGRAM_BINARY_LENGTH, &program_len); + r_program_binary->resize(program_len); + PoolByteArray::Write w = r_program_binary->write(); + glGetProgramBinary(p_ids.main, program_len, NULL, r_program_format, w.ptr()); + } - glUseProgram(v.id); + return true; +} +void ShaderGLES3::_setup_uniforms(CustomCode *p_cc) const { //print_line("uniforms: "); for (int j = 0; j < uniform_count; j++) { - v.uniform_location[j] = glGetUniformLocation(v.id, uniform_names[j]); - //print_line("uniform "+String(uniform_names[j])+" location "+itos(v.uniform_location[j])); + version->uniform_location[j] = glGetUniformLocation(version->ids.main, uniform_names[j]); + //print_line("uniform "+String(uniform_names[j])+" location "+itos(version->uniform_location[j])); } // set texture uniforms for (int i = 0; i < texunit_pair_count; i++) { - GLint loc = glGetUniformLocation(v.id, texunit_pairs[i].name); + GLint loc = glGetUniformLocation(version->ids.main, texunit_pairs[i].name); if (loc >= 0) { if (texunit_pairs[i].index < 0) { glUniform1i(loc, max_image_units + texunit_pairs[i].index); //negative, goes down @@ -509,33 +864,41 @@ ShaderGLES3::Version *ShaderGLES3::get_current_version() { // assign uniform block bind points for (int i = 0; i < ubo_count; i++) { - GLint loc = glGetUniformBlockIndex(v.id, ubo_pairs[i].name); - if (loc >= 0) { - glUniformBlockBinding(v.id, loc, ubo_pairs[i].index); - } + GLint loc = glGetUniformBlockIndex(version->ids.main, ubo_pairs[i].name); + if (loc >= 0) + glUniformBlockBinding(version->ids.main, loc, ubo_pairs[i].index); } - if (cc) { - v.texture_uniform_locations.resize(cc->texture_uniforms.size()); - for (int i = 0; i < cc->texture_uniforms.size(); i++) { - v.texture_uniform_locations.write[i] = glGetUniformLocation(v.id, String(cc->texture_uniforms[i]).ascii().get_data()); - glUniform1i(v.texture_uniform_locations[i], i + base_material_tex_index); + if (p_cc) { + version->texture_uniform_locations.resize(p_cc->texture_uniforms.size()); + for (int i = 0; i < p_cc->texture_uniforms.size(); i++) { + version->texture_uniform_locations.write[i] = glGetUniformLocation(version->ids.main, String(p_cc->texture_uniforms[i]).ascii().get_data()); + glUniform1i(version->texture_uniform_locations[i], i + base_material_tex_index); } } +} - glUseProgram(0); - - v.ok = true; - if (cc) { - cc->versions.insert(conditional_version.version); +void ShaderGLES3::_dispose_program(Version *p_version) { + if (compile_queue) { + if (p_version->compile_status == Version::COMPILE_STATUS_PROCESSING_AT_QUEUE) { + compile_queue->cancel(p_version->ids.main); + } } + glDeleteShader(p_version->ids.vert); + glDeleteShader(p_version->ids.frag); + glDeleteProgram(p_version->ids.main); - return &v; + if (p_version->compiling_list.in_list()) { + p_version->compiling_list.remove_from_list(); + versions_compiling_count--; + } + p_version->compile_status = Version::COMPILE_STATUS_ERROR; + ERR_FAIL_COND(versions_compiling_count < 0); } GLint ShaderGLES3::get_uniform_location(const String &p_name) const { ERR_FAIL_COND_V(!version, -1); - return glGetUniformLocation(version->id, p_name.ascii().get_data()); + return glGetUniformLocation(version->ids.main, p_name.ascii().get_data()); } void ShaderGLES3::setup(const char **p_conditional_defines, int p_conditional_count, const char **p_uniform_names, int p_uniform_count, const AttributePair *p_attribute_pairs, int p_attribute_count, const TexUnitPair *p_texunit_pairs, int p_texunit_pair_count, const UBOPair *p_ubo_pairs, int p_ubo_pair_count, const Feedback *p_feedback, int p_feedback_count, const char *p_vertex_code, const char *p_fragment_code, int p_vertex_code_start, int p_fragment_code_start) { @@ -640,26 +1003,33 @@ void ShaderGLES3::setup(const char **p_conditional_defines, int p_conditional_co glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &max_image_units); } +void ShaderGLES3::setup_async_compilation(ThreadedCallableQueue *p_compile_queue, bool p_parallel_compile_supported, uint32_t p_code_id, DecideFallbackModeFunc p_decide_fallback_mode_func) { + compile_queue = p_compile_queue; + parallel_compile_supported = p_parallel_compile_supported; + fallback_code_id = p_code_id; + decide_fallback_mode_func = p_decide_fallback_mode_func; +} + void ShaderGLES3::finish() { const VersionKey *V = nullptr; while ((V = version_map.next(V))) { Version &v = version_map[*V]; - glDeleteShader(v.vert_id); - glDeleteShader(v.frag_id); - glDeleteProgram(v.id); + _dispose_program(&v); memdelete_arr(v.uniform_location); } + ERR_FAIL_COND(versions_compiling.first()); + ERR_FAIL_COND(versions_compiling_count != 0); } void ShaderGLES3::clear_caches() { const VersionKey *V = nullptr; while ((V = version_map.next(V))) { Version &v = version_map[*V]; - glDeleteShader(v.vert_id); - glDeleteShader(v.frag_id); - glDeleteProgram(v.id); + _dispose_program(&v); memdelete_arr(v.uniform_location); } + ERR_FAIL_COND(versions_compiling.first()); + ERR_FAIL_COND(versions_compiling_count != 0); version_map.clear(); @@ -675,7 +1045,7 @@ uint32_t ShaderGLES3::create_custom_shader() { return last_custom_code++; } -void ShaderGLES3::set_custom_shader_code(uint32_t p_code_id, const String &p_vertex, const String &p_vertex_globals, const String &p_fragment, const String &p_light, const String &p_fragment_globals, const String &p_uniforms, const Vector &p_texture_uniforms, const Vector &p_custom_defines) { +void ShaderGLES3::set_custom_shader_code(uint32_t p_code_id, const String &p_vertex, const String &p_vertex_globals, const String &p_fragment, const String &p_light, const String &p_fragment_globals, const String &p_uniforms, const Vector &p_texture_uniforms, const Vector &p_custom_defines, int p_fallback_mode_hint) { ERR_FAIL_COND(!custom_code_map.has(p_code_id)); CustomCode *cc = &custom_code_map[p_code_id]; @@ -687,6 +1057,7 @@ void ShaderGLES3::set_custom_shader_code(uint32_t p_code_id, const String &p_ver cc->texture_uniforms = p_texture_uniforms; cc->uniforms = p_uniforms; cc->custom_defines = p_custom_defines; + cc->fallback_mode_hint = p_fallback_mode_hint; cc->version++; } @@ -708,11 +1079,8 @@ void ShaderGLES3::free_custom_shader(uint32_t p_code_id) { ERR_CONTINUE(!version_map.has(key)); Version &v = version_map[key]; - glDeleteShader(v.vert_id); - glDeleteShader(v.frag_id); - glDeleteProgram(v.id); + _dispose_program(&v); memdelete_arr(v.uniform_location); - v.id = 0; version_map.erase(key); } @@ -729,6 +1097,10 @@ ShaderGLES3::ShaderGLES3() { last_custom_code = 1; uniforms_dirty = true; base_material_tex_index = 0; + compile_queue = nullptr; + parallel_compile_supported = false; + fallback_code_id = 0; + decide_fallback_mode_func = nullptr; } ShaderGLES3::~ShaderGLES3() { diff --git a/drivers/gles3/shader_gles3.h b/drivers/gles3/shader_gles3.h index e0f34e889bd9..10bc21c240be 100644 --- a/drivers/gles3/shader_gles3.h +++ b/drivers/gles3/shader_gles3.h @@ -32,8 +32,11 @@ #define SHADER_GLES3_H #include "core/hash_map.h" +#include "core/local_vector.h" #include "core/map.h" #include "core/math/camera_matrix.h" +#include "core/safe_refcount.h" +#include "core/self_list.h" #include "core/variant.h" #include "platform_config.h" @@ -45,6 +48,10 @@ #include +template +class ThreadedCallableQueue; +class ShaderCacheGLES3; + class ShaderGLES3 { protected: struct Enum { @@ -103,28 +110,103 @@ class ShaderGLES3 { String fragment_globals; String light; String uniforms; + int fallback_mode_hint; uint32_t version; Vector texture_uniforms; Vector custom_defines; Set versions; }; +public: + static ShaderCacheGLES3 *shader_cache; + static ThreadedCallableQueue *cache_write_queue; + + enum FallbackMode { + FALLBACK_MODE_NONE, + FALLBACK_MODE_NO_RENDER, + FALLBACK_MODE_SIMPLE, + }; + + using DecideFallbackModeFunc = void (*)(uint32_t p_conditionals, int p_fallback_mode_hint, int *r_fallback_mode, uint32_t *r_simple_fallback_version); + + static bool force_no_render_fallback; +#ifdef DEBUG_ENABLED + static bool force_use_fallbacks; +#endif + static bool force_sync_compile; + static int *shader_compiles_started_this_frame; + static int max_shaders_compiling; + static uint64_t current_frame; + + static void advance_async_shaders_compilation(); + +private: + static int versions_compiling_count; + struct Version { - GLuint id; - GLuint vert_id; - GLuint frag_id; + uint64_t version_key; + + // Set by the render thread upfront; the compile thread (for queued async.) reads them + struct Ids { + GLuint main; + GLuint vert; + GLuint frag; + } ids; + + ShaderGLES3 *shader; + uint32_t code_version; + + int fallback_mode; + uint32_t simple_fallback_version; // Conditionals for the simple fallback version if fallback mode is simple GLint *uniform_location; Vector texture_uniform_locations; - uint32_t code_version; - bool ok; + bool uniforms_ready; + uint64_t last_frame_processed; + + enum CompileStatus { + COMPILE_STATUS_PENDING, + COMPILE_STATUS_SOURCE_PROVIDED, + COMPILE_STATUS_COMPILING, + COMPILE_STATUS_PROCESSING_AT_QUEUE, + COMPILE_STATUS_BINARY_READY, + COMPILE_STATUS_BINARY_READY_FROM_CACHE, + COMPILE_STATUS_LINKING, + COMPILE_STATUS_ERROR, + COMPILE_STATUS_RESTART_NEEDED, + COMPILE_STATUS_OK, + }; + CompileStatus compile_status; + SelfList compiling_list; + + struct ProgramBinary { + String cache_hash; + enum Source { + SOURCE_NONE, + SOURCE_LOCAL, // Binary data will only be available if cache enabled + SOURCE_QUEUE, + SOURCE_CACHE, + } source; + // Shared with the compile thread (for queued async.); otherwise render thread only + GLenum format; + PoolByteArray data; + SafeNumeric result_from_queue; + } program_binary; + Version() : - id(0), - vert_id(0), - frag_id(0), - uniform_location(nullptr), + version_key(0), + ids(), + shader(nullptr), code_version(0), - ok(false) {} + fallback_mode(FALLBACK_MODE_NONE), + simple_fallback_version(0), + uniform_location(nullptr), + uniforms_ready(false), + last_frame_processed(UINT64_MAX), + compile_status(COMPILE_STATUS_PENDING), + compiling_list(this), + program_binary() {} }; + static SelfList::List versions_compiling; Version *version; @@ -176,7 +258,21 @@ class ShaderGLES3 { int base_material_tex_index; + ThreadedCallableQueue *compile_queue; // Non-null if using queued asynchronous compilation (via seconday context) + bool parallel_compile_supported; // True if using natively supported asyncrhonous compilation + uint32_t fallback_code_id; + DecideFallbackModeFunc decide_fallback_mode_func; + Version *get_current_version(); + // These will run on the shader compile thread if using que compile queue approach to async. + void _set_source(Version::Ids p_ids, const LocalVector &p_vertex_strings, const LocalVector &p_fragment_strings) const; + void _compile(Version::Ids p_ids) const; + bool _complete_compile(Version::Ids p_ids, bool p_retrievable) const; + bool _complete_link(Version::Ids p_ids, GLenum *r_program_format = nullptr, PoolByteArray *r_program_binary = nullptr) const; + // --- + bool _process_program_state(); + void _setup_uniforms(CustomCode *p_cc) const; + void _dispose_program(Version *p_version); static ShaderGLES3 *active; @@ -274,6 +370,8 @@ class ShaderGLES3 { Map uniform_defaults; Map uniform_cameras; + bool _bind(bool p_fallback_forbidden, bool *r_fallen_back = nullptr); + protected: _FORCE_INLINE_ int _get_uniform(int p_which) const; _FORCE_INLINE_ void _set_conditional(int p_which, bool p_value); @@ -291,16 +389,14 @@ class ShaderGLES3 { GLint get_uniform_location(int p_index) const; static _FORCE_INLINE_ ShaderGLES3 *get_active() { return active; }; - bool bind(); + bool bind(bool *r_fallen_back = nullptr); void unbind(); void bind_uniforms(); - inline GLuint get_program() const { return version ? version->id : 0; } - void clear_caches(); uint32_t create_custom_shader(); - void set_custom_shader_code(uint32_t p_code_id, const String &p_vertex, const String &p_vertex_globals, const String &p_fragment, const String &p_light, const String &p_fragment_globals, const String &p_uniforms, const Vector &p_texture_uniforms, const Vector &p_custom_defines); + void set_custom_shader_code(uint32_t p_code_id, const String &p_vertex, const String &p_vertex_globals, const String &p_fragment, const String &p_light, const String &p_fragment_globals, const String &p_uniforms, const Vector &p_texture_uniforms, const Vector &p_custom_defines, int p_fallback_mode_hint); void set_custom_shader(uint32_t p_code_id); void free_custom_shader(uint32_t p_code_id); @@ -314,7 +410,8 @@ class ShaderGLES3 { } uint32_t get_version() const { return new_conditional_version.version; } - _FORCE_INLINE_ bool is_version_valid() const { return version && version->ok; } + void set_version(uint32_t p_version) { new_conditional_version.version = p_version; } + _FORCE_INLINE_ bool is_version_valid() const { return version && version->compile_status == Version::COMPILE_STATUS_OK; } void set_uniform_camera(int p_idx, const CameraMatrix &p_mat) { uniform_cameras[p_idx] = p_mat; @@ -334,6 +431,7 @@ class ShaderGLES3 { } virtual void init() = 0; + void setup_async_compilation(ThreadedCallableQueue *p_compile_queue, bool p_parallel_compile_supported, uint32_t p_code_id, DecideFallbackModeFunc p_decide_fallback_mode_func); void finish(); void set_base_material_tex_index(int p_idx); diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index 607a31b18541..8085095095c4 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -302,6 +302,11 @@ MATERIAL_UNIFORMS #endif +#ifdef IS_SIMPLE_FALLBACK +uniform vec3 simple_fallback_uv1_scale; +uniform vec3 simple_fallback_uv1_offset; +#endif + /* clang-format off */ VERTEX_SHADER_GLOBALS @@ -462,6 +467,12 @@ void main() { float point_size = 1.0; highp mat4 modelview = camera_inverse_matrix * world_matrix; + +#if defined(IS_SIMPLE_FALLBACK) + { + uv_interp = ((uv_interp * simple_fallback_uv1_scale.xy) + simple_fallback_uv1_offset.xy); + } +#endif { /* clang-format off */ @@ -717,6 +728,10 @@ MATERIAL_UNIFORMS #endif +#ifdef IS_SIMPLE_FALLBACK +uniform vec4 simple_fallback_albedo_color; +#endif + layout(std140) uniform SceneData { highp mat4 projection_matrix; highp mat4 inv_projection_matrix; @@ -1796,6 +1811,13 @@ void main() { float sss_strength = 0.0; #endif +#if defined(IS_SIMPLE_FALLBACK) + { + vec4 albedo_sample = texture(m_simple_fallback_albedo_tex, uv_interp); + albedo = simple_fallback_albedo_color.rgb * albedo_sample.rgb; + alpha = simple_fallback_albedo_color.a * albedo_sample.a; + } +#endif { /* clang-format off */ diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp index 4f38e953bdc1..5c0fe287a103 100644 --- a/editor/editor_export.cpp +++ b/editor/editor_export.cpp @@ -268,6 +268,11 @@ void EditorExportPlatform::gen_debug_flags(Vector &r_flags, int p_flags) if (p_flags & DEBUG_FLAG_VIEW_NAVIGATION) { r_flags.push_back("--debug-navigation"); } + + if (p_flags & DEBUG_FLAG_ASYNC_SHADERS) { + + r_flags.push_back("--debug-shader-fallbacks"); + } } Error EditorExportPlatform::_save_pack_file(void *p_userdata, const String &p_path, const Vector &p_data, int p_file, int p_total) { diff --git a/editor/editor_export.h b/editor/editor_export.h index 0c0f62effe8f..bfa545cbd463 100644 --- a/editor/editor_export.h +++ b/editor/editor_export.h @@ -246,6 +246,7 @@ class EditorExportPlatform : public Reference { DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST = 4, DEBUG_FLAG_VIEW_COLLISONS = 8, DEBUG_FLAG_VIEW_NAVIGATION = 16, + DEBUG_FLAG_ASYNC_SHADERS = 32, }; virtual Error run(const Ref &p_preset, int p_device, int p_debug_flags) { return OK; } diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index ea188b327351..834c54d595ef 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -2736,6 +2736,14 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { editor_run.set_debug_navigation(!ischecked); EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_debug_navigation", !ischecked); + } break; + case RUN_DEBUG_SHADER_FALLBACKS: { + bool ischecked = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_SHADER_FALLBACKS)); + debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_SHADER_FALLBACKS), !ischecked); + run_native->set_debug_async_shaders(!ischecked); + editor_run.set_debug_async_shaders(!ischecked); + EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_debug_shader_fallbacks", !ischecked); + } break; case RUN_RELOAD_SCRIPTS: { bool ischecked = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(RUN_RELOAD_SCRIPTS)); @@ -2993,6 +3001,7 @@ void EditorNode::_update_debug_options() { bool check_file_server = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_file_server", false); bool check_debug_collisons = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_collisons", false); bool check_debug_navigation = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_navigation", false); + bool check_debug_shader_fallbacks = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_shader_fallbacks", false); bool check_live_debug = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_live_debug", true); bool check_reload_scripts = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_reload_scripts", true); @@ -3008,6 +3017,9 @@ void EditorNode::_update_debug_options() { if (check_debug_navigation) { _menu_option_confirm(RUN_DEBUG_NAVIGATION, true); } + if (check_debug_shader_fallbacks) { + _menu_option_confirm(RUN_DEBUG_SHADER_FALLBACKS, true); + } if (check_live_debug) { _menu_option_confirm(RUN_LIVE_DEBUG, true); } @@ -6334,6 +6346,10 @@ EditorNode::EditorNode() { p->get_item_count() - 1, TTR("When this option is enabled, collision shapes and raycast nodes (for 2D and 3D) will be visible in the running project.")); p->add_check_shortcut(ED_SHORTCUT("editor/visible_navigation", TTR("Visible Navigation")), RUN_DEBUG_NAVIGATION); + p->set_item_tooltip( + p->get_item_count() - 1, + TTR("When this option is enabled, shaders that have degradation enabled will be displayed in their fallback form all the time.")); + p->add_check_shortcut(ED_SHORTCUT("editor/use_shader_fallbacks", TTR("Force Shader Fallbacks")), RUN_DEBUG_SHADER_FALLBACKS); p->set_item_tooltip( p->get_item_count() - 1, TTR("When this option is enabled, navigation meshes and polygons will be visible in the running project.")); diff --git a/editor/editor_node.h b/editor/editor_node.h index 7ac9a16b07aa..b793bb184fdd 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -170,6 +170,7 @@ class EditorNode : public Node { RUN_LIVE_DEBUG, RUN_DEBUG_COLLISONS, RUN_DEBUG_NAVIGATION, + RUN_DEBUG_SHADER_FALLBACKS, RUN_DEPLOY_REMOTE_DEBUG, RUN_RELOAD_SCRIPTS, RUN_VCS_SETTINGS, diff --git a/editor/editor_run.cpp b/editor/editor_run.cpp index e6f04ea3cd7a..9e61a3b884bc 100644 --- a/editor/editor_run.cpp +++ b/editor/editor_run.cpp @@ -75,6 +75,10 @@ Error EditorRun::run(const String &p_scene, const String &p_custom_args, const L args.push_back("--debug-navigation"); } + if (debug_async_shaders) { + args.push_back("--debug-shader-fallbacks"); + } + int screen = EditorSettings::get_singleton()->get("run/window_placement/screen"); if (screen == 0) { // Same as editor @@ -273,9 +277,20 @@ bool EditorRun::get_debug_navigation() const { return debug_navigation; } +void EditorRun::set_debug_async_shaders(bool p_debug) { + + debug_async_shaders = p_debug; +} + +bool EditorRun::get_debug_async_shaders() const { + + return debug_async_shaders; +} + EditorRun::EditorRun() { status = STATUS_STOP; running_scene = ""; debug_collisions = false; debug_navigation = false; + debug_async_shaders = false; } diff --git a/editor/editor_run.h b/editor/editor_run.h index efcf3f53e30a..46e64559e58f 100644 --- a/editor/editor_run.h +++ b/editor/editor_run.h @@ -47,6 +47,7 @@ class EditorRun { private: bool debug_collisions; bool debug_navigation; + bool debug_async_shaders; Status status; String running_scene; @@ -65,6 +66,9 @@ class EditorRun { void set_debug_navigation(bool p_debug); bool get_debug_navigation() const; + void set_debug_async_shaders(bool p_debug); + bool get_debug_async_shaders() const; + EditorRun(); }; diff --git a/editor/editor_run_native.cpp b/editor/editor_run_native.cpp index b9e15d335e23..263a7bdd46ea 100644 --- a/editor/editor_run_native.cpp +++ b/editor/editor_run_native.cpp @@ -142,6 +142,9 @@ void EditorRunNative::_run_native(int p_idx, int p_platform) { if (debug_navigation) { flags |= EditorExportPlatform::DEBUG_FLAG_VIEW_NAVIGATION; } + if (debug_async_shaders) { + flags |= EditorExportPlatform::DEBUG_FLAG_ASYNC_SHADERS; + } eep->run(preset, p_idx, flags); } @@ -188,6 +191,14 @@ bool EditorRunNative::get_debug_navigation() const { return debug_navigation; } +void EditorRunNative::set_debug_async_shaders(bool p_debug) { + debug_async_shaders = p_debug; +} + +bool EditorRunNative::get_debug_async_shaders() const { + return debug_async_shaders; +} + EditorRunNative::EditorRunNative() { set_process(true); first = true; @@ -195,6 +206,7 @@ EditorRunNative::EditorRunNative() { deploy_debug_remote = false; debug_collisions = false; debug_navigation = false; + debug_async_shaders = false; resume_idx = 0; resume_platform = 0; } diff --git a/editor/editor_run_native.h b/editor/editor_run_native.h index a93736112963..fc6737bd2a90 100644 --- a/editor/editor_run_native.h +++ b/editor/editor_run_native.h @@ -43,6 +43,7 @@ class EditorRunNative : public HBoxContainer { bool deploy_debug_remote; bool debug_collisions; bool debug_navigation; + bool debug_async_shaders; int resume_idx; int resume_platform; @@ -66,6 +67,9 @@ class EditorRunNative : public HBoxContainer { void set_debug_navigation(bool p_debug); bool get_debug_navigation() const; + void set_debug_async_shaders(bool p_debug); + bool get_debug_async_shaders() const; + void resume_run_native(); EditorRunNative(); diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 207acb42b5a7..978443d7c26b 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -657,7 +657,6 @@ void EditorSettings::_load_defaults(Ref p_extra_config) { _initial_set("project_manager/sorting_order", 0); hints["project_manager/sorting_order"] = PropertyInfo(Variant::INT, "project_manager/sorting_order", PROPERTY_HINT_ENUM, "Name,Path,Last Modified"); - if (p_extra_config.is_valid()) { if (p_extra_config->has_section("init_projects") && p_extra_config->has_section_key("init_projects", "list")) { Vector list = p_extra_config->get_value("init_projects", "list"); diff --git a/platform/iphone/display_layer.mm b/platform/iphone/display_layer.mm index d99dcddf5394..23af5c121c8a 100644 --- a/platform/iphone/display_layer.mm +++ b/platform/iphone/display_layer.mm @@ -53,6 +53,7 @@ @implementation GodotOpenGLLayer { GLint backingHeight; EAGLContext *context; + EAGLContext *context_offscreen; GLuint viewRenderbuffer, viewFramebuffer; GLuint depthRenderbuffer; } @@ -75,6 +76,9 @@ - (void)initializeDisplayLayer { gles3_available = false; fallback_gl2 = true; NSLog(@"Failed to create OpenGL ES 3.0 context. Falling back to OpenGL ES 2.0"); + } else { + context_offscreen = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; + OSIPhone::get_singleton()->set_offscreen_gl_context(context_offscreen); } } @@ -130,6 +134,9 @@ - (void)dealloc { if (context) { context = nil; } + if (context_offscreen) { + context_offscreen = nil; + } } - (BOOL)createFramebuffer { diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h index a7a70c335281..4f23d7c01384 100644 --- a/platform/iphone/os_iphone.h +++ b/platform/iphone/os_iphone.h @@ -61,6 +61,8 @@ class OSIPhone : public OS_Unix { VideoMode video_mode; + EAGLContext *offscreen_gl_context; + virtual int get_video_driver_count() const; virtual const char *get_video_driver_name(int p_driver) const; @@ -162,6 +164,10 @@ class OSIPhone : public OS_Unix { virtual void get_fullscreen_mode_list(List *p_list, int p_screen = 0) const; + void set_offscreen_gl_context(EAGLContext *p_context); + virtual bool is_offscreen_gl_available() const; + virtual void set_offscreen_gl_current(bool p_current); + virtual void set_keep_screen_on(bool p_enabled); virtual bool can_draw() const; diff --git a/platform/iphone/os_iphone.mm b/platform/iphone/os_iphone.mm index c115bc099766..f6e288c20652 100644 --- a/platform/iphone/os_iphone.mm +++ b/platform/iphone/os_iphone.mm @@ -417,6 +417,25 @@ void register_dynamic_symbol(char *name, void *address) { p_list->push_back(video_mode); } +void OSIPhone::set_offscreen_gl_context(EAGLContext *p_context) { + + offscreen_gl_context = p_context; +} + +bool OSIPhone::is_offscreen_gl_available() const { + + return offscreen_gl_context; +} + +void OSIPhone::set_offscreen_gl_current(bool p_current) { + + if (p_current) { + [EAGLContext setCurrentContext:offscreen_gl_context]; + } else { + [EAGLContext setCurrentContext:nil]; + } +} + bool OSIPhone::can_draw() const { if (native_video_is_playing()) return false; @@ -683,6 +702,7 @@ void add_ios_init_callback(init_callback cb) { main_loop = NULL; visual_server = NULL; + offscreen_gl_context = NULL; // can't call set_data_dir from here, since it requires DirAccess // which is initialized in initialize_core diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h index 206de92dc3e6..9ab6bbe66b26 100644 --- a/platform/osx/os_osx.h +++ b/platform/osx/os_osx.h @@ -115,6 +115,7 @@ class OS_OSX : public OS_Unix { id cursor; NSOpenGLPixelFormat *pixelFormat; NSOpenGLContext *context; + NSOpenGLContext *context_offscreen; Vector mpath; bool layered_window; @@ -247,6 +248,9 @@ class OS_OSX : public OS_Unix { virtual VideoMode get_video_mode(int p_screen = 0) const; virtual void get_fullscreen_mode_list(List *p_list, int p_screen = 0) const; + virtual bool is_offscreen_gl_available() const; + virtual void set_offscreen_gl_current(bool p_current); + virtual String get_executable_path() const; virtual LatinKeyboardVariant get_latin_keyboard_variant() const; diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index 2aff957aef6c..0216456ea526 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -1674,6 +1674,8 @@ static void displays_arrangement_changed(CGDirectDisplayID display_id, CGDisplay [window_view setOpenGLContext:context]; + context_offscreen = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil]; + [context makeCurrentContext]; GLint dim[2]; @@ -2423,6 +2425,18 @@ virtual void log_error(const char *p_function, const char *p_file, int p_line, c void OS_OSX::get_fullscreen_mode_list(List *p_list, int p_screen) const { } +bool OS_OSX::is_offscreen_gl_available() const { + return context_offscreen != nil; +} + +void OS_OSX::set_offscreen_gl_current(bool p_current) { + if (p_current) { + [context makeCurrentContext]; + } else { + [NSOpenGLContext clearCurrentContext]; + } +} + int OS_OSX::get_screen_count() const { NSArray *screenArray = [NSScreen screens]; return [screenArray count]; @@ -3353,6 +3367,7 @@ void _update_keyboard_layouts() { OS_OSX::OS_OSX() { context = nullptr; + context_offscreen = nullptr; memset(cursors, 0, sizeof(cursors)); key_event_pos = 0; diff --git a/platform/windows/context_gl_windows.cpp b/platform/windows/context_gl_windows.cpp index 32200119da18..a5920740e497 100644 --- a/platform/windows/context_gl_windows.cpp +++ b/platform/windows/context_gl_windows.cpp @@ -58,6 +58,21 @@ void ContextGL_Windows::make_current() { wglMakeCurrent(hDC, hRC); } +bool ContextGL_Windows::is_offscreen_available() const { + + return hRC_offscreen != NULL; +} + +void ContextGL_Windows::make_offscreen_current() { + + ERR_FAIL_COND(!wglMakeCurrent(hDC, hRC_offscreen)); +} + +void ContextGL_Windows::release_offscreen_current() { + + ERR_FAIL_COND(!wglMakeCurrent(hDC, NULL)); +} + HDC ContextGL_Windows::get_hdc() { return hDC; } @@ -205,6 +220,8 @@ Error ContextGL_Windows::initialize() { { return ERR_CANT_CREATE; // Return FALSE } + + hRC_offscreen = wglCreateContextAttribsARB(hDC, 0, attribs); } wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT"); @@ -217,6 +234,7 @@ Error ContextGL_Windows::initialize() { ContextGL_Windows::ContextGL_Windows(HWND hwnd, bool p_opengl_3_context) { opengl_3_context = p_opengl_3_context; hWnd = hwnd; + hRC_offscreen = NULL; use_vsync = false; vsync_via_compositor = false; } diff --git a/platform/windows/context_gl_windows.h b/platform/windows/context_gl_windows.h index d838c9404427..5866d45f30d8 100644 --- a/platform/windows/context_gl_windows.h +++ b/platform/windows/context_gl_windows.h @@ -46,6 +46,7 @@ typedef int(APIENTRY *PFNWGLGETSWAPINTERVALEXTPROC)(void); class ContextGL_Windows { HDC hDC; HGLRC hRC; + HGLRC hRC_offscreen; unsigned int pixel_format; HWND hWnd; bool opengl_3_context; @@ -62,6 +63,10 @@ class ContextGL_Windows { void make_current(); + bool is_offscreen_available() const; + void make_offscreen_current(); + void release_offscreen_current(); + HDC get_hdc(); HGLRC get_hglrc(); diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 9f3477e59903..bc2908334d07 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -1676,6 +1676,24 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int return OK; } +bool OS_Windows::is_offscreen_gl_available() const { +#if defined(OPENGL_ENABLED) + return gl_context->is_offscreen_available(); +#else + return false; +#endif +} + +void OS_Windows::set_offscreen_gl_current(bool p_current) { +#if defined(OPENGL_ENABLED) + if (p_current) { + return gl_context->make_offscreen_current(); + } else { + return gl_context->release_offscreen_current(); + } +#endif +} + void OS_Windows::set_clipboard(const String &p_text) { // Convert LF line endings to CRLF in clipboard content // Otherwise, line endings won't be visible when pasted in other software diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index 41b152123a44..3ac263d9e11c 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -384,6 +384,9 @@ class OS_Windows : public OS { virtual void initialize_core(); virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); + virtual bool is_offscreen_gl_available() const; + virtual void set_offscreen_gl_current(bool p_current); + virtual void set_main_loop(MainLoop *p_main_loop); virtual void delete_main_loop(); diff --git a/platform/x11/context_gl_x11.cpp b/platform/x11/context_gl_x11.cpp index a6cca238c2c9..ac9a9d177002 100644 --- a/platform/x11/context_gl_x11.cpp +++ b/platform/x11/context_gl_x11.cpp @@ -47,6 +47,7 @@ typedef GLXContext (*GLXCREATECONTEXTATTRIBSARBPROC)(Display *, GLXFBConfig, GLX struct ContextGL_X11_Private { ::GLXContext glx_context; + ::GLXContext glx_context_offscreen; }; void ContextGL_X11::release_current() { @@ -57,6 +58,21 @@ void ContextGL_X11::make_current() { glXMakeCurrent(x11_display, x11_window, p->glx_context); } +bool ContextGL_X11::is_offscreen_available() const { + + return p->glx_context_offscreen; +} + +void ContextGL_X11::make_offscreen_current() { + + glXMakeCurrent(x11_display, x11_window, p->glx_context_offscreen); +} + +void ContextGL_X11::release_offscreen_current() { + + glXMakeCurrent(x11_display, None, NULL); +} + void ContextGL_X11::swap_buffers() { glXSwapBuffers(x11_display, x11_window); } @@ -181,6 +197,7 @@ Error ContextGL_X11::initialize() { p->glx_context = glXCreateContextAttribsARB(x11_display, fbconfig, nullptr, true, context_attribs); ERR_FAIL_COND_V(ctxErrorOccurred || !p->glx_context, ERR_UNCONFIGURED); + p->glx_context_offscreen = glXCreateContextAttribsARB(x11_display, fbconfig, NULL, true, context_attribs); } break; } @@ -275,12 +292,16 @@ ContextGL_X11::ContextGL_X11(::Display *p_x11_display, ::Window &p_x11_window, c glx_minor = glx_major = 0; p = memnew(ContextGL_X11_Private); p->glx_context = nullptr; + p->glx_context_offscreen = nullptr; use_vsync = false; } ContextGL_X11::~ContextGL_X11() { release_current(); glXDestroyContext(x11_display, p->glx_context); + if (p->glx_context_offscreen) { + glXDestroyContext(x11_display, p->glx_context_offscreen); + } memdelete(p); } diff --git a/platform/x11/context_gl_x11.h b/platform/x11/context_gl_x11.h index cc9e2dd81cde..90fff4180fb4 100644 --- a/platform/x11/context_gl_x11.h +++ b/platform/x11/context_gl_x11.h @@ -69,6 +69,10 @@ class ContextGL_X11 { int get_window_height(); void *get_glx_context(); + bool is_offscreen_available() const; + void make_offscreen_current(); + void release_offscreen_current(); + Error initialize(); void set_use_vsync(bool p_use); diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index 00d50bf31ec0..56718b6a8c1a 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -878,6 +878,24 @@ void OS_X11::finalize() { args.clear(); } +bool OS_X11::is_offscreen_gl_available() const { +#if defined(OPENGL_ENABLED) + return context_gl->is_offscreen_available(); +#else + return false; +#endif +} + +void OS_X11::set_offscreen_gl_current(bool p_current) { +#if defined(OPENGL_ENABLED) + if (p_current) { + return context_gl->make_offscreen_current(); + } else { + return context_gl->release_offscreen_current(); + } +#endif +} + void OS_X11::set_mouse_mode(MouseMode p_mode) { if (p_mode == mouse_mode) { return; diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h index 96d848bebfb2..e0a0db584789 100644 --- a/platform/x11/os_x11.h +++ b/platform/x11/os_x11.h @@ -238,6 +238,9 @@ class OS_X11 : public OS_Unix { virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); virtual void finalize(); + virtual bool is_offscreen_gl_available() const; + virtual void set_offscreen_gl_current(bool p_current); + virtual void set_main_loop(MainLoop *p_main_loop); void _window_changed(XEvent *event); diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 7abdf774965e..5b5f89e836b6 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -390,6 +390,8 @@ void SpatialMaterial::_update_shader() { // Add a comment to describe the shader origin (useful when converting to ShaderMaterial). String code = "// NOTE: Shader automatically converted from " VERSION_NAME " " VERSION_FULL_CONFIG "'s SpatialMaterial.\n\n"; + bool can_fall_back_to_simple = true; + code += "shader_type spatial;\nrender_mode "; switch (blend_mode) { case BLEND_MODE_MIX: @@ -504,6 +506,7 @@ void SpatialMaterial::_update_shader() { code += "uniform float metallic;\n"; if (grow_enabled) { code += "uniform float grow;\n"; + can_fall_back_to_simple = false; } if (proximity_fade_enabled) { @@ -645,6 +648,8 @@ void SpatialMaterial::_update_shader() { if (flags[FLAG_BILLBOARD_KEEP_SCALE]) { code += "\tMODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(length(WORLD_MATRIX[0].xyz), 0.0, 0.0, 0.0),vec4(0.0, length(WORLD_MATRIX[1].xyz), 0.0, 0.0),vec4(0.0, 0.0, length(WORLD_MATRIX[2].xyz), 0.0),vec4(0.0, 0.0, 0.0, 1.0));\n"; } + + can_fall_back_to_simple = false; } break; case BILLBOARD_FIXED_Y: { code += "\tMODELVIEW_MATRIX = INV_CAMERA_MATRIX * mat4(vec4(normalize(cross(vec3(0.0, 1.0, 0.0), CAMERA_MATRIX[2].xyz)),0.0),vec4(0.0, 1.0, 0.0, 0.0),vec4(normalize(cross(CAMERA_MATRIX[0].xyz, vec3(0.0, 1.0, 0.0))),0.0),WORLD_MATRIX[3]);\n"; @@ -652,6 +657,8 @@ void SpatialMaterial::_update_shader() { if (flags[FLAG_BILLBOARD_KEEP_SCALE]) { code += "\tMODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(length(WORLD_MATRIX[0].xyz), 0.0, 0.0, 0.0),vec4(0.0, length(WORLD_MATRIX[1].xyz), 0.0, 0.0),vec4(0.0, 0.0, length(WORLD_MATRIX[2].xyz), 0.0),vec4(0.0, 0.0, 0.0, 1.0));\n"; } + + can_fall_back_to_simple = false; } break; case BILLBOARD_PARTICLES: { //make billboard @@ -673,6 +680,8 @@ void SpatialMaterial::_update_shader() { code += "\t}"; code += "\tUV /= vec2(h_frames, v_frames);\n"; code += "\tUV += vec2(mod(particle_frame, h_frames) / h_frames, floor(particle_frame / h_frames) / v_frames);\n"; + + can_fall_back_to_simple = false; } break; } @@ -692,6 +701,8 @@ void SpatialMaterial::_update_shader() { code += "\t\tMODELVIEW_MATRIX[1]*=sc;\n"; code += "\t\tMODELVIEW_MATRIX[2]*=sc;\n"; code += "\t}\n"; + + can_fall_back_to_simple = false; } if (detail_uv == DETAIL_UV_2 && !flags[FLAG_UV2_USE_TRIPLANAR]) { @@ -803,6 +814,7 @@ void SpatialMaterial::_update_shader() { if (flags[FLAG_ALBEDO_FROM_VERTEX_COLOR]) { code += "\talbedo_tex *= COLOR;\n"; + can_fall_back_to_simple = false; } code += "\tALBEDO = albedo.rgb * albedo_tex.rgb;\n"; @@ -1047,6 +1059,20 @@ void SpatialMaterial::_update_shader() { code += "}\n"; + String fallback_mode_str; + switch (fallback_mode) { + case FALLBACK_MODE_NONE: { + fallback_mode_str = "fallback_none"; + } break; + case FALLBACK_MODE_NO_RENDER: { + fallback_mode_str = "fallback_no_render"; + } break; + case FALLBACK_MODE_SIMPLE: { + fallback_mode_str = can_fall_back_to_simple ? "fallback_simple" : "fallback_none"; + } break; + } + code = code.replace_first("render_mode ", "render_mode " + fallback_mode_str + ","); + ShaderData shader_data; shader_data.shader = VS::get_singleton()->shader_create(); shader_data.users = 1; @@ -1822,6 +1848,18 @@ Shader::Mode SpatialMaterial::get_shader_mode() const { return Shader::MODE_SPATIAL; } +void SpatialMaterial::set_fallback_mode(FallbackMode p_mode) { + + fallback_mode = p_mode; + _queue_shader_change(); + _change_notify(); +} + +SpatialMaterial::FallbackMode SpatialMaterial::get_fallback_mode() const { + + return fallback_mode; +} + void SpatialMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_albedo", "albedo"), &SpatialMaterial::set_albedo); ClassDB::bind_method(D_METHOD("get_albedo"), &SpatialMaterial::get_albedo); @@ -1994,6 +2032,9 @@ void SpatialMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_distance_fade_min_distance", "distance"), &SpatialMaterial::set_distance_fade_min_distance); ClassDB::bind_method(D_METHOD("get_distance_fade_min_distance"), &SpatialMaterial::get_distance_fade_min_distance); + ClassDB::bind_method(D_METHOD("set_fallback_mode", "mode"), &SpatialMaterial::set_fallback_mode); + ClassDB::bind_method(D_METHOD("get_fallback_mode"), &SpatialMaterial::get_fallback_mode); + ADD_GROUP("Flags", "flags_"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_transparent"), "set_feature", "get_feature", FEATURE_TRANSPARENT); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_use_shadow_to_opacity"), "set_flag", "get_flag", FLAG_USE_SHADOW_TO_OPACITY); @@ -2136,6 +2177,8 @@ void SpatialMaterial::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::REAL, "distance_fade_min_distance", PROPERTY_HINT_RANGE, "0,4096,0.01"), "set_distance_fade_min_distance", "get_distance_fade_min_distance"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "distance_fade_max_distance", PROPERTY_HINT_RANGE, "0,4096,0.01"), "set_distance_fade_max_distance", "get_distance_fade_max_distance"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "fallback_mode", PROPERTY_HINT_ENUM, "None,No render,Simple"), "set_fallback_mode", "get_fallback_mode"); + BIND_ENUM_CONSTANT(TEXTURE_ALBEDO); BIND_ENUM_CONSTANT(TEXTURE_METALLIC); BIND_ENUM_CONSTANT(TEXTURE_ROUGHNESS); @@ -2236,6 +2279,10 @@ void SpatialMaterial::_bind_methods() { BIND_ENUM_CONSTANT(DISTANCE_FADE_PIXEL_ALPHA); BIND_ENUM_CONSTANT(DISTANCE_FADE_PIXEL_DITHER); BIND_ENUM_CONSTANT(DISTANCE_FADE_OBJECT_DITHER); + + BIND_ENUM_CONSTANT(FALLBACK_MODE_NONE); + BIND_ENUM_CONSTANT(FALLBACK_MODE_NO_RENDER); + BIND_ENUM_CONSTANT(FALLBACK_MODE_SIMPLE); } SpatialMaterial::SpatialMaterial() : @@ -2309,6 +2356,8 @@ SpatialMaterial::SpatialMaterial() : diffuse_mode = DIFFUSE_BURLEY; specular_mode = SPECULAR_SCHLICK_GGX; + fallback_mode = FALLBACK_MODE_SIMPLE; + for (int i = 0; i < FEATURE_MAX; i++) { features[i] = false; } diff --git a/scene/resources/material.h b/scene/resources/material.h index 94ab38304e5f..275f3166d5a2 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -237,6 +237,12 @@ class SpatialMaterial : public Material { DISTANCE_FADE_OBJECT_DITHER, }; + enum FallbackMode { + FALLBACK_MODE_NONE, + FALLBACK_MODE_NO_RENDER, + FALLBACK_MODE_SIMPLE, + }; + private: union MaterialKey { struct { @@ -425,6 +431,7 @@ class SpatialMaterial : public Material { DiffuseMode diffuse_mode; BillboardMode billboard_mode; EmissionOperator emission_op; + FallbackMode fallback_mode; TextureChannel metallic_texture_channel; TextureChannel roughness_texture_channel; @@ -622,6 +629,9 @@ class SpatialMaterial : public Material { void set_refraction_texture_channel(TextureChannel p_channel); TextureChannel get_refraction_texture_channel() const; + void set_fallback_mode(FallbackMode p_mode); + FallbackMode get_fallback_mode() const; + static void init_shaders(); static void finish_shaders(); static void flush_changes(); @@ -649,6 +659,7 @@ VARIANT_ENUM_CAST(SpatialMaterial::BillboardMode) VARIANT_ENUM_CAST(SpatialMaterial::TextureChannel) VARIANT_ENUM_CAST(SpatialMaterial::EmissionOperator) VARIANT_ENUM_CAST(SpatialMaterial::DistanceFadeMode) +VARIANT_ENUM_CAST(SpatialMaterial::FallbackMode) ////////////////////// diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index 6516cd93e56b..4ddac0e90448 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -897,6 +897,7 @@ VisualShader::RenderModeEnums VisualShader::render_mode_enums[] = { { Shader::MODE_SPATIAL, "cull" }, { Shader::MODE_SPATIAL, "diffuse" }, { Shader::MODE_SPATIAL, "specular" }, + { Shader::MODE_SPATIAL, "fallback" }, { Shader::MODE_CANVAS_ITEM, "blend" }, { Shader::MODE_CANVAS_ITEM, nullptr } }; diff --git a/servers/visual/rasterizer.h b/servers/visual/rasterizer.h index 0f846affc813..4552e0319afc 100644 --- a/servers/visual/rasterizer.h +++ b/servers/visual/rasterizer.h @@ -248,6 +248,9 @@ class RasterizerStorage { virtual void shader_get_custom_defines(RID p_shader, Vector *p_defines) const = 0; virtual void shader_remove_custom_define(RID p_shader, const String &p_define) = 0; + virtual void set_forced_sync_shader_compile_enabled(bool p_enabled) = 0; + virtual bool is_forced_sync_shader_compile_enabled() = 0; + /* COMMON MATERIAL API */ virtual RID material_create() = 0; diff --git a/servers/visual/shader_types.cpp b/servers/visual/shader_types.cpp index ce09b84a7daf..e2d0cf6c7469 100644 --- a/servers/visual/shader_types.cpp +++ b/servers/visual/shader_types.cpp @@ -197,6 +197,10 @@ ShaderTypes::ShaderTypes() { shader_modes[VS::SHADER_SPATIAL].modes.push_back("vertex_lighting"); + shader_modes[VS::SHADER_SPATIAL].modes.push_back("fallback_simple"); + shader_modes[VS::SHADER_SPATIAL].modes.push_back("fallback_no_render"); + shader_modes[VS::SHADER_SPATIAL].modes.push_back("fallback_none"); + /************ CANVAS ITEM **************************/ shader_modes[VS::SHADER_CANVAS_ITEM].functions["global"].built_ins["TIME"] = constt(ShaderLanguage::TYPE_FLOAT); diff --git a/servers/visual/visual_server_raster.h b/servers/visual/visual_server_raster.h index a8a0baf0eea8..0c142d13f922 100644 --- a/servers/visual/visual_server_raster.h +++ b/servers/visual/visual_server_raster.h @@ -192,6 +192,8 @@ class VisualServerRaster : public VisualServer { BIND2C(shader_get_custom_defines, RID, Vector *) BIND2(shader_remove_custom_define, RID, const String &) + BIND1(set_forced_sync_shader_compile_enabled, bool) + /* COMMON MATERIAL API */ BIND0R(RID, material_create) diff --git a/servers/visual/visual_server_scene.cpp b/servers/visual/visual_server_scene.cpp index c3206562737a..91ec548fab39 100644 --- a/servers/visual/visual_server_scene.cpp +++ b/servers/visual/visual_server_scene.cpp @@ -2875,7 +2875,11 @@ bool VisualServerScene::_render_reflection_probe_step(Instance *p_instance, int } _prepare_scene(xform, cm, false, RID(), VSG::storage->reflection_probe_get_cull_mask(p_instance->base), p_instance->scenario->self, shadow_atlas, reflection_probe->instance, reflection_probe->previous_room_id_hint); + + bool forced_sync_backup = VSG::storage->is_forced_sync_shader_compile_enabled(); + VSG::storage->set_forced_sync_shader_compile_enabled(true); _render_scene(xform, cm, 0, false, RID(), p_instance->scenario->self, shadow_atlas, reflection_probe->instance, p_step); + VSG::storage->set_forced_sync_shader_compile_enabled(forced_sync_backup); } else { //do roughness postprocess step until it believes it's done diff --git a/servers/visual/visual_server_wrap_mt.h b/servers/visual/visual_server_wrap_mt.h index 5f412f143067..73436e77f3cb 100644 --- a/servers/visual/visual_server_wrap_mt.h +++ b/servers/visual/visual_server_wrap_mt.h @@ -130,6 +130,8 @@ class VisualServerWrapMT : public VisualServer { FUNC2SC(shader_get_custom_defines, RID, Vector *) FUNC2(shader_remove_custom_define, RID, const String &) + FUNC1(set_forced_sync_shader_compile_enabled, bool) + /* COMMON MATERIAL API */ FUNCRID(material) diff --git a/servers/visual_server.cpp b/servers/visual_server.cpp index 7f6aaff334ee..765e1446a961 100644 --- a/servers/visual_server.cpp +++ b/servers/visual_server.cpp @@ -1868,6 +1868,7 @@ void VisualServer::_bind_methods() { ClassDB::bind_method(D_METHOD("shader_get_param_list", "shader"), &VisualServer::_shader_get_param_list_bind); ClassDB::bind_method(D_METHOD("shader_set_default_texture_param", "shader", "name", "texture"), &VisualServer::shader_set_default_texture_param); ClassDB::bind_method(D_METHOD("shader_get_default_texture_param", "shader", "name"), &VisualServer::shader_get_default_texture_param); + ClassDB::bind_method(D_METHOD("set_forced_sync_shader_compile_enabled", "enabled"), &VisualServer::set_forced_sync_shader_compile_enabled); ClassDB::bind_method(D_METHOD("material_create"), &VisualServer::material_create); ClassDB::bind_method(D_METHOD("material_set_shader", "shader_material", "shader"), &VisualServer::material_set_shader); diff --git a/servers/visual_server.h b/servers/visual_server.h index 306bb7bf5a86..1670a516b9f5 100644 --- a/servers/visual_server.h +++ b/servers/visual_server.h @@ -204,6 +204,8 @@ class VisualServer : public Object { virtual void shader_get_custom_defines(RID p_shader, Vector *p_defines) const = 0; virtual void shader_remove_custom_define(RID p_shader, const String &p_define) = 0; + virtual void set_forced_sync_shader_compile_enabled(bool p_enabled) = 0; + /* COMMON MATERIAL API */ enum { diff --git a/thirdparty/glad/glad.c b/thirdparty/glad/glad.c index dc1b8cb697e6..836fbee09bef 100644 --- a/thirdparty/glad/glad.c +++ b/thirdparty/glad/glad.c @@ -1,6 +1,6 @@ /* - OpenGL loader generated by glad 0.1.34 on Tue Nov 17 16:41:02 2020. + OpenGL loader generated by glad 0.1.34 on Fri Feb 19 21:01:51 2021. Language/Generator: C/C++ Specification: gl @@ -9,18 +9,21 @@ Extensions: GL_ARB_debug_output, GL_ARB_framebuffer_object, + GL_ARB_get_program_binary, + GL_ARB_parallel_shader_compile, GL_EXT_framebuffer_blit, GL_EXT_framebuffer_multisample, - GL_EXT_framebuffer_object + GL_EXT_framebuffer_object, + GL_KHR_parallel_shader_compile Loader: True Local files: False Omit khrplatform: False Reproducible: False Commandline: - --profile="compatibility" --api="gl=3.3" --generator="c" --spec="gl" --extensions="GL_ARB_debug_output,GL_ARB_framebuffer_object,GL_EXT_framebuffer_blit,GL_EXT_framebuffer_multisample,GL_EXT_framebuffer_object" + --profile="compatibility" --api="gl=3.3" --generator="c" --spec="gl" --extensions="GL_ARB_debug_output,GL_ARB_framebuffer_object,GL_ARB_get_program_binary,GL_ARB_parallel_shader_compile,GL_EXT_framebuffer_blit,GL_EXT_framebuffer_multisample,GL_EXT_framebuffer_object,GL_KHR_parallel_shader_compile" Online: - https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D3.3&extensions=GL_ARB_debug_output&extensions=GL_ARB_framebuffer_object&extensions=GL_EXT_framebuffer_blit&extensions=GL_EXT_framebuffer_multisample&extensions=GL_EXT_framebuffer_object + https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D3.3&extensions=GL_ARB_debug_output&extensions=GL_ARB_framebuffer_object&extensions=GL_ARB_get_program_binary&extensions=GL_ARB_parallel_shader_compile&extensions=GL_EXT_framebuffer_blit&extensions=GL_EXT_framebuffer_multisample&extensions=GL_EXT_framebuffer_object&extensions=GL_KHR_parallel_shader_compile */ #include @@ -997,13 +1000,20 @@ PFNGLWINDOWPOS3SPROC glad_glWindowPos3s = NULL; PFNGLWINDOWPOS3SVPROC glad_glWindowPos3sv = NULL; int GLAD_GL_ARB_debug_output = 0; int GLAD_GL_ARB_framebuffer_object = 0; +int GLAD_GL_ARB_get_program_binary = 0; +int GLAD_GL_ARB_parallel_shader_compile = 0; int GLAD_GL_EXT_framebuffer_blit = 0; int GLAD_GL_EXT_framebuffer_multisample = 0; int GLAD_GL_EXT_framebuffer_object = 0; +int GLAD_GL_KHR_parallel_shader_compile = 0; PFNGLDEBUGMESSAGECONTROLARBPROC glad_glDebugMessageControlARB = NULL; PFNGLDEBUGMESSAGEINSERTARBPROC glad_glDebugMessageInsertARB = NULL; PFNGLDEBUGMESSAGECALLBACKARBPROC glad_glDebugMessageCallbackARB = NULL; PFNGLGETDEBUGMESSAGELOGARBPROC glad_glGetDebugMessageLogARB = NULL; +PFNGLGETPROGRAMBINARYPROC glad_glGetProgramBinary = NULL; +PFNGLPROGRAMBINARYPROC glad_glProgramBinary = NULL; +PFNGLPROGRAMPARAMETERIPROC glad_glProgramParameteri = NULL; +PFNGLMAXSHADERCOMPILERTHREADSARBPROC glad_glMaxShaderCompilerThreadsARB = NULL; PFNGLBLITFRAMEBUFFEREXTPROC glad_glBlitFramebufferEXT = NULL; PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glad_glRenderbufferStorageMultisampleEXT = NULL; PFNGLISRENDERBUFFEREXTPROC glad_glIsRenderbufferEXT = NULL; @@ -1023,6 +1033,7 @@ PFNGLFRAMEBUFFERTEXTURE3DEXTPROC glad_glFramebufferTexture3DEXT = NULL; PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC glad_glFramebufferRenderbufferEXT = NULL; PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC glad_glGetFramebufferAttachmentParameterivEXT = NULL; PFNGLGENERATEMIPMAPEXTPROC glad_glGenerateMipmapEXT = NULL; +PFNGLMAXSHADERCOMPILERTHREADSKHRPROC glad_glMaxShaderCompilerThreadsKHR = NULL; static void load_GL_VERSION_1_0(GLADloadproc load) { if(!GLAD_GL_VERSION_1_0) return; glad_glCullFace = (PFNGLCULLFACEPROC)load("glCullFace"); @@ -1816,6 +1827,16 @@ static void load_GL_ARB_framebuffer_object(GLADloadproc load) { glad_glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)load("glRenderbufferStorageMultisample"); glad_glFramebufferTextureLayer = (PFNGLFRAMEBUFFERTEXTURELAYERPROC)load("glFramebufferTextureLayer"); } +static void load_GL_ARB_get_program_binary(GLADloadproc load) { + if(!GLAD_GL_ARB_get_program_binary) return; + glad_glGetProgramBinary = (PFNGLGETPROGRAMBINARYPROC)load("glGetProgramBinary"); + glad_glProgramBinary = (PFNGLPROGRAMBINARYPROC)load("glProgramBinary"); + glad_glProgramParameteri = (PFNGLPROGRAMPARAMETERIPROC)load("glProgramParameteri"); +} +static void load_GL_ARB_parallel_shader_compile(GLADloadproc load) { + if(!GLAD_GL_ARB_parallel_shader_compile) return; + glad_glMaxShaderCompilerThreadsARB = (PFNGLMAXSHADERCOMPILERTHREADSARBPROC)load("glMaxShaderCompilerThreadsARB"); +} static void load_GL_EXT_framebuffer_blit(GLADloadproc load) { if(!GLAD_GL_EXT_framebuffer_blit) return; glad_glBlitFramebufferEXT = (PFNGLBLITFRAMEBUFFEREXTPROC)load("glBlitFramebufferEXT"); @@ -1844,13 +1865,20 @@ static void load_GL_EXT_framebuffer_object(GLADloadproc load) { glad_glGetFramebufferAttachmentParameterivEXT = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC)load("glGetFramebufferAttachmentParameterivEXT"); glad_glGenerateMipmapEXT = (PFNGLGENERATEMIPMAPEXTPROC)load("glGenerateMipmapEXT"); } +static void load_GL_KHR_parallel_shader_compile(GLADloadproc load) { + if(!GLAD_GL_KHR_parallel_shader_compile) return; + glad_glMaxShaderCompilerThreadsKHR = (PFNGLMAXSHADERCOMPILERTHREADSKHRPROC)load("glMaxShaderCompilerThreadsKHR"); +} static int find_extensionsGL(void) { if (!get_exts()) return 0; GLAD_GL_ARB_debug_output = has_ext("GL_ARB_debug_output"); GLAD_GL_ARB_framebuffer_object = has_ext("GL_ARB_framebuffer_object"); + GLAD_GL_ARB_get_program_binary = has_ext("GL_ARB_get_program_binary"); + GLAD_GL_ARB_parallel_shader_compile = has_ext("GL_ARB_parallel_shader_compile"); GLAD_GL_EXT_framebuffer_blit = has_ext("GL_EXT_framebuffer_blit"); GLAD_GL_EXT_framebuffer_multisample = has_ext("GL_EXT_framebuffer_multisample"); GLAD_GL_EXT_framebuffer_object = has_ext("GL_EXT_framebuffer_object"); + GLAD_GL_KHR_parallel_shader_compile = has_ext("GL_KHR_parallel_shader_compile"); free_exts(); return 1; } @@ -1931,9 +1959,11 @@ int gladLoadGLLoader(GLADloadproc load) { if (!find_extensionsGL()) return 0; load_GL_ARB_debug_output(load); load_GL_ARB_framebuffer_object(load); + load_GL_ARB_get_program_binary(load); + load_GL_ARB_parallel_shader_compile(load); load_GL_EXT_framebuffer_blit(load); load_GL_EXT_framebuffer_multisample(load); load_GL_EXT_framebuffer_object(load); + load_GL_KHR_parallel_shader_compile(load); return GLVersion.major != 0 || GLVersion.minor != 0; } - diff --git a/thirdparty/glad/glad/glad.h b/thirdparty/glad/glad/glad.h index f211e6aa5709..b328522e1271 100644 --- a/thirdparty/glad/glad/glad.h +++ b/thirdparty/glad/glad/glad.h @@ -1,6 +1,6 @@ /* - OpenGL loader generated by glad 0.1.34 on Tue Nov 17 16:41:02 2020. + OpenGL loader generated by glad 0.1.34 on Fri Feb 19 21:01:51 2021. Language/Generator: C/C++ Specification: gl @@ -9,18 +9,21 @@ Extensions: GL_ARB_debug_output, GL_ARB_framebuffer_object, + GL_ARB_get_program_binary, + GL_ARB_parallel_shader_compile, GL_EXT_framebuffer_blit, GL_EXT_framebuffer_multisample, - GL_EXT_framebuffer_object + GL_EXT_framebuffer_object, + GL_KHR_parallel_shader_compile Loader: True Local files: False Omit khrplatform: False Reproducible: False Commandline: - --profile="compatibility" --api="gl=3.3" --generator="c" --spec="gl" --extensions="GL_ARB_debug_output,GL_ARB_framebuffer_object,GL_EXT_framebuffer_blit,GL_EXT_framebuffer_multisample,GL_EXT_framebuffer_object" + --profile="compatibility" --api="gl=3.3" --generator="c" --spec="gl" --extensions="GL_ARB_debug_output,GL_ARB_framebuffer_object,GL_ARB_get_program_binary,GL_ARB_parallel_shader_compile,GL_EXT_framebuffer_blit,GL_EXT_framebuffer_multisample,GL_EXT_framebuffer_object,GL_KHR_parallel_shader_compile" Online: - https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D3.3&extensions=GL_ARB_debug_output&extensions=GL_ARB_framebuffer_object&extensions=GL_EXT_framebuffer_blit&extensions=GL_EXT_framebuffer_multisample&extensions=GL_EXT_framebuffer_object + https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D3.3&extensions=GL_ARB_debug_output&extensions=GL_ARB_framebuffer_object&extensions=GL_ARB_get_program_binary&extensions=GL_ARB_parallel_shader_compile&extensions=GL_EXT_framebuffer_blit&extensions=GL_EXT_framebuffer_multisample&extensions=GL_EXT_framebuffer_object&extensions=GL_KHR_parallel_shader_compile */ @@ -3629,6 +3632,12 @@ GLAPI PFNGLSECONDARYCOLORP3UIVPROC glad_glSecondaryColorP3uiv; #define GL_DEBUG_SEVERITY_HIGH_ARB 0x9146 #define GL_DEBUG_SEVERITY_MEDIUM_ARB 0x9147 #define GL_DEBUG_SEVERITY_LOW_ARB 0x9148 +#define GL_PROGRAM_BINARY_RETRIEVABLE_HINT 0x8257 +#define GL_PROGRAM_BINARY_LENGTH 0x8741 +#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE +#define GL_PROGRAM_BINARY_FORMATS 0x87FF +#define GL_MAX_SHADER_COMPILER_THREADS_ARB 0x91B0 +#define GL_COMPLETION_STATUS_ARB 0x91B1 #define GL_READ_FRAMEBUFFER_EXT 0x8CA8 #define GL_DRAW_FRAMEBUFFER_EXT 0x8CA9 #define GL_DRAW_FRAMEBUFFER_BINDING_EXT 0x8CA6 @@ -3687,6 +3696,8 @@ GLAPI PFNGLSECONDARYCOLORP3UIVPROC glad_glSecondaryColorP3uiv; #define GL_RENDERBUFFER_ALPHA_SIZE_EXT 0x8D53 #define GL_RENDERBUFFER_DEPTH_SIZE_EXT 0x8D54 #define GL_RENDERBUFFER_STENCIL_SIZE_EXT 0x8D55 +#define GL_MAX_SHADER_COMPILER_THREADS_KHR 0x91B0 +#define GL_COMPLETION_STATUS_KHR 0x91B1 #ifndef GL_ARB_debug_output #define GL_ARB_debug_output 1 GLAPI int GLAD_GL_ARB_debug_output; @@ -3707,6 +3718,26 @@ GLAPI PFNGLGETDEBUGMESSAGELOGARBPROC glad_glGetDebugMessageLogARB; #define GL_ARB_framebuffer_object 1 GLAPI int GLAD_GL_ARB_framebuffer_object; #endif +#ifndef GL_ARB_get_program_binary +#define GL_ARB_get_program_binary 1 +GLAPI int GLAD_GL_ARB_get_program_binary; +typedef void (APIENTRYP PFNGLGETPROGRAMBINARYPROC)(GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary); +GLAPI PFNGLGETPROGRAMBINARYPROC glad_glGetProgramBinary; +#define glGetProgramBinary glad_glGetProgramBinary +typedef void (APIENTRYP PFNGLPROGRAMBINARYPROC)(GLuint program, GLenum binaryFormat, const void *binary, GLsizei length); +GLAPI PFNGLPROGRAMBINARYPROC glad_glProgramBinary; +#define glProgramBinary glad_glProgramBinary +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIPROC)(GLuint program, GLenum pname, GLint value); +GLAPI PFNGLPROGRAMPARAMETERIPROC glad_glProgramParameteri; +#define glProgramParameteri glad_glProgramParameteri +#endif +#ifndef GL_ARB_parallel_shader_compile +#define GL_ARB_parallel_shader_compile 1 +GLAPI int GLAD_GL_ARB_parallel_shader_compile; +typedef void (APIENTRYP PFNGLMAXSHADERCOMPILERTHREADSARBPROC)(GLuint count); +GLAPI PFNGLMAXSHADERCOMPILERTHREADSARBPROC glad_glMaxShaderCompilerThreadsARB; +#define glMaxShaderCompilerThreadsARB glad_glMaxShaderCompilerThreadsARB +#endif #ifndef GL_EXT_framebuffer_blit #define GL_EXT_framebuffer_blit 1 GLAPI int GLAD_GL_EXT_framebuffer_blit; @@ -3776,6 +3807,13 @@ typedef void (APIENTRYP PFNGLGENERATEMIPMAPEXTPROC)(GLenum target); GLAPI PFNGLGENERATEMIPMAPEXTPROC glad_glGenerateMipmapEXT; #define glGenerateMipmapEXT glad_glGenerateMipmapEXT #endif +#ifndef GL_KHR_parallel_shader_compile +#define GL_KHR_parallel_shader_compile 1 +GLAPI int GLAD_GL_KHR_parallel_shader_compile; +typedef void (APIENTRYP PFNGLMAXSHADERCOMPILERTHREADSKHRPROC)(GLuint count); +GLAPI PFNGLMAXSHADERCOMPILERTHREADSKHRPROC glad_glMaxShaderCompilerThreadsKHR; +#define glMaxShaderCompilerThreadsKHR glad_glMaxShaderCompilerThreadsKHR +#endif #ifdef __cplusplus }