Implement async shader compilation plus caching for GL ES 3

Async. compilation via ubershader is currently available in the scene and particles shaders only.

Bonus:
- Use `#if defined()` syntax for not true conditionals, so they don't unnecessarily take a bit in the version flagset.
- Remove unused `ENABLE_CLIP_ALPHA` from scene shader.
- Remove unused `PARTICLES_COPY` from the particles shader.
- Remove unused uniform related code.
- Shader language/compiler: use ordered hash maps for deterministic code generation (needed for caching).
This commit is contained in:
Pedro J. Estébanez 2021-09-26 21:31:17 +02:00
parent b6f04dfd21
commit 4c710780d4
43 changed files with 2169 additions and 669 deletions

View file

@ -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 <functional>
template <class K>
class ThreadedCallableQueue {
public:
using Job = std::function<void()>;
private:
bool exit;
Thread thread;
BinaryMutex mutex;
Semaphore sem;
OrderedHashMap<K, Job> 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 <class K>
void ThreadedCallableQueue<K>::_thread_func(void *p_user_data) {
ThreadedCallableQueue *self = static_cast<ThreadedCallableQueue *>(p_user_data);
while (true) {
self->sem.wait();
self->mutex.lock();
if (self->exit) {
self->mutex.unlock();
break;
}
typename OrderedHashMap<K, Job>::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<Job> 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<K, Job>::Element E = self->queue.front(); E; E = E.next()) {
Job job = E.value();
job();
}
self->mutex.unlock();
}
template <class K>
void ThreadedCallableQueue<K>::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 <class K>
void ThreadedCallableQueue<K>::cancel(K p_key) {
MutexLock lock(mutex);
ERR_FAIL_COND(exit);
if (queue.erase(p_key)) {
sem.wait();
}
}
template <class K>
ThreadedCallableQueue<K>::ThreadedCallableQueue() :
exit(false) {
thread.start(&_thread_func, this);
}
template <class K>
ThreadedCallableQueue<K>::~ThreadedCallableQueue() {
exit = true;
sem.post();
thread.wait_to_finish();
}
#endif // THREADED_CALLABLE_QUEUE_H

View file

@ -1222,6 +1222,39 @@
If [code]true[/code] and available on the target Android 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.
</member>
<member name="rendering/gles3/shaders/log_active_async_compiles_count" type="bool" setter="" getter="" default="false">
If [code]true[/code], every time an asynchronous shader compilation or an asynchronous shader reconstruction from cache starts or finishes, a line will be logged telling how many of those are happening.
If the platform doesn't support parallel shader compile, but only the compile queue via a secondary GL context, what the message will tell is the number of shader compiles currently queued.
[b]Note:[/b] This setting is only meaningful if [code]rendering/gles3/shaders/shader_compilation_mode[/code] is [b]not[/b] [code]Synchronous[/code].
</member>
<member name="rendering/gles3/shaders/max_simultaneous_compiles" type="int" setter="" getter="" default="2">
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 a conservative one, so you are advised to tweak it according to the hardware you are targeting.
[b]Note:[/b] This setting is only meaningful if [code]rendering/gles3/shaders/shader_compilation_mode[/code] is [b]not[/b] [code]Synchronous[/code].
</member>
<member name="rendering/gles3/shaders/max_simultaneous_compiles.mobile" type="int" setter="" getter="" default="1">
The default is a very conservative override for [code]rendering/gles3/shaders/max_concurrent_compiles[/code].
Depending on the specific devices you are targeting, you may want to raise it.
[b]Note:[/b] This setting is only meaningful if [code]rendering/gles3/shaders/shader_compilation_mode[/code] is [b]not[/b] [code]Synchronous[/code].
</member>
<member name="rendering/gles3/shaders/shader_cache_size_mb" type="int" setter="" getter="" default="512">
The maximum size, in megabytes, that the ubershader cache can grow up to. On startup, the least recently used entries will be deleted until the total size is within bounds.
[b]Note:[/b] This setting is only meaningful if [code]rendering/gles3/shaders/shader_compilation_mode[/code] is set to [code]Asynchronous + Cache[/code].
</member>
<member name="rendering/gles3/shaders/shader_cache_size_mb.mobile" type="int" setter="" getter="" default="128">
An override for [code]rendering/gles3/shaders/ubershader_cache_size_mb[/code], so a smaller maximum size can be configured for mobile platforms, where storage space is more limited.
[b]Note:[/b] This setting is only meaningful if [code]rendering/gles3/shaders/shader_compilation_mode[/code] is set to [code]Asynchronous + Cache[/code].
</member>
<member name="rendering/gles3/shaders/shader_compilation_mode" type="int" setter="" getter="" default="0">
If set to [code]Asynchronous[/code] and available on the target device, asynchronous compilation of shaders is enabled (in contrast to [code]Asynchronous[/code]).
That means that when a shader is first used under some new rendering situation, the game won't stall while such shader is being compiled. Instead, a 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.
Depending on the async mode configured for a given material/shader, the fallback will be an "ubershader" (the default) or just skip rendering any item it is applied to.
An ubershader is a very complex shader, slow but suited to any rendering situation, that the engine generates internally so it can be used from the beginning while the traditional conditioned, optimized version of it is being compiled.
In order to save some loading time, you can use [code]Asynchronous + Cache[/code], which also causes the ubershaders to be cached into storage so they can be ready faster next time they are used (provided the platform provides support for it).
[b]Warning:[/b] Async. compilation is currently only supported for spatial and particle materials/shaders.
</member>
<member name="rendering/limits/buffers/blend_shape_max_buffer_size_kb" type="int" setter="" getter="" default="4096">
Max buffer size for blend shapes. Any blend shape bigger than this will not work.
</member>

View file

@ -87,6 +87,10 @@
<member name="ao_texture_channel" type="int" setter="set_ao_texture_channel" getter="get_ao_texture_channel" enum="SpatialMaterial.TextureChannel">
Specifies the channel of the [member ao_texture] in which the ambient occlusion information is stored. This is useful when you store the information for multiple effects in a single texture. For example if you stored metallic in the red channel, roughness in the blue, and ambient occlusion in the green you could reduce the number of textures you use.
</member>
<member name="async_mode" type="int" setter="set_async_mode" getter="get_async_mode" enum="SpatialMaterial.AsyncMode" default="0">
If [member ProjectSettings.rendering/gles3/shaders/shader_compilation_mode] is [code]Synchronous[/code] (with or without cache), this determines how this material must behave in regards to asynchronous shader compilation.
[constant ASYNC_MODE_VISIBLE] is the default and the best for most cases.
</member>
<member name="clearcoat" type="float" setter="set_clearcoat" getter="get_clearcoat">
Sets the strength of the clearcoat effect. Setting to [code]0[/code] looks the same as disabling the clearcoat effect.
</member>
@ -639,5 +643,12 @@
<constant name="DISTANCE_FADE_OBJECT_DITHER" value="3" enum="DistanceFadeMode">
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].
</constant>
<constant name="ASYNC_MODE_VISIBLE" value="0" enum="AsyncMode">
The real conditioned shader needed on each situation will be sent for background compilation. In the meantime, a very complex shader that adapts to every situation will be used ("ubershader"). This ubershader is much slower to render, but will keep the game running without stalling to compile. Once shader compilation is done, the ubershader is replaced by the traditional optimized shader.
</constant>
<constant name="ASYNC_MODE_HIDDEN" value="1" enum="AsyncMode">
Anything with this material applied won't be rendered while this material's shader is being compiled.
This is useful for optimization, in cases where the visuals won't suffer from having certain non-essential elements missing during the short time their shaders are being compiled.
</constant>
</constants>
</class>

View file

@ -2525,6 +2525,15 @@
Sets the default clear color which is used when a specific clear color has not been selected.
</description>
</method>
<method name="set_shader_async_hidden_forbidden">
<return type="void" />
<argument index="0" name="forbidden" type="bool" />
<description>
If asynchronous shader compilation is enabled, this controls whether [constant SpatialMaterial.ASYNC_MODE_HIDDEN] is obeyed.
For instance, you may want to enable this temporarily before taking a screenshot. This ensures everything is visible even if shaders with async mode [i]hidden[/i] are not ready yet.
Reflection probes use this internally to ensure they capture everything regardless the shaders are ready or not.
</description>
</method>
<method name="set_shader_time_scale">
<return type="void" />
<argument index="0" name="scale" type="float" />

View file

@ -266,6 +266,9 @@ public:
void shader_get_custom_defines(RID p_shader, Vector<String> *p_defines) const {}
void shader_remove_custom_define(RID p_shader, const String &p_define) {}
void set_shader_async_hidden_forbidden(bool p_forbidden) {}
bool is_shader_async_hidden_forbidden() { return false; }
/* COMMON MATERIAL API */
RID material_create() { return RID(); }

View file

@ -544,6 +544,9 @@ public:
virtual void shader_get_custom_defines(RID p_shader, Vector<String> *p_defines) const;
virtual void shader_remove_custom_define(RID p_shader, const String &p_define);
void set_shader_async_hidden_forbidden(bool p_forbidden) {}
bool is_shader_async_hidden_forbidden() { return false; }
void _update_shader(Shader *p_shader) const;
void update_dirty_shaders();

View file

@ -295,8 +295,8 @@ String ShaderCompilerGLES2::_dump_node_code(const SL::Node *p_node, int p_level,
int max_texture_uniforms = 0;
int max_uniforms = 0;
for (Map<StringName, SL::ShaderNode::Uniform>::Element *E = snode->uniforms.front(); E; E = E->next()) {
if (SL::is_sampler_type(E->get().type)) {
for (OrderedHashMap<StringName, SL::ShaderNode::Uniform>::Element E = snode->uniforms.front(); E; E = E.next()) {
if (SL::is_sampler_type(E.get().type)) {
max_texture_uniforms++;
} else {
max_uniforms++;
@ -347,55 +347,55 @@ String ShaderCompilerGLES2::_dump_node_code(const SL::Node *p_node, int p_level,
// uniforms
for (Map<StringName, SL::ShaderNode::Uniform>::Element *E = snode->uniforms.front(); E; E = E->next()) {
for (OrderedHashMap<StringName, SL::ShaderNode::Uniform>::Element E = snode->uniforms.front(); E; E = E.next()) {
StringBuffer<> uniform_code;
// use highp if no precision is specified to prevent different default values in fragment and vertex shader
SL::DataPrecision precision = E->get().precision;
if (precision == SL::PRECISION_DEFAULT && E->get().type != SL::TYPE_BOOL) {
SL::DataPrecision precision = E.get().precision;
if (precision == SL::PRECISION_DEFAULT && E.get().type != SL::TYPE_BOOL) {
precision = SL::PRECISION_HIGHP;
}
uniform_code += "uniform ";
uniform_code += _prestr(precision);
uniform_code += _typestr(E->get().type);
uniform_code += _typestr(E.get().type);
uniform_code += " ";
uniform_code += _mkid(E->key());
uniform_code += _mkid(E.key());
uniform_code += ";\n";
if (SL::is_sampler_type(E->get().type)) {
r_gen_code.texture_uniforms.write[E->get().texture_order] = E->key();
r_gen_code.texture_hints.write[E->get().texture_order] = E->get().hint;
if (SL::is_sampler_type(E.get().type)) {
r_gen_code.texture_uniforms.write[E.get().texture_order] = E.key();
r_gen_code.texture_hints.write[E.get().texture_order] = E.get().hint;
} else {
r_gen_code.uniforms.write[E->get().order] = E->key();
r_gen_code.uniforms.write[E.get().order] = E.key();
}
vertex_global += uniform_code.as_string();
fragment_global += uniform_code.as_string();
p_actions.uniforms->insert(E->key(), E->get());
p_actions.uniforms->insert(E.key(), E.get());
}
// varyings
List<Pair<StringName, SL::ShaderNode::Varying>> var_frag_to_light;
for (Map<StringName, SL::ShaderNode::Varying>::Element *E = snode->varyings.front(); E; E = E->next()) {
if (E->get().stage == SL::ShaderNode::Varying::STAGE_FRAGMENT_TO_LIGHT || E->get().stage == SL::ShaderNode::Varying::STAGE_FRAGMENT) {
var_frag_to_light.push_back(Pair<StringName, SL::ShaderNode::Varying>(E->key(), E->get()));
fragment_varyings.insert(E->key());
for (OrderedHashMap<StringName, SL::ShaderNode::Varying>::Element E = snode->varyings.front(); E; E = E.next()) {
if (E.get().stage == SL::ShaderNode::Varying::STAGE_FRAGMENT_TO_LIGHT || E.get().stage == SL::ShaderNode::Varying::STAGE_FRAGMENT) {
var_frag_to_light.push_back(Pair<StringName, SL::ShaderNode::Varying>(E.key(), E.get()));
fragment_varyings.insert(E.key());
continue;
}
StringBuffer<> varying_code;
varying_code += "varying ";
varying_code += _prestr(E->get().precision);
varying_code += _typestr(E->get().type);
varying_code += _prestr(E.get().precision);
varying_code += _typestr(E.get().type);
varying_code += " ";
varying_code += _mkid(E->key());
if (E->get().array_size > 0) {
varying_code += _mkid(E.key());
if (E.get().array_size > 0) {
varying_code += "[";
varying_code += itos(E->get().array_size);
varying_code += itos(E.get().array_size);
varying_code += "]";
}
varying_code += ";\n";

View file

@ -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.shader_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 {
@ -487,6 +492,8 @@ RasterizerGLES3::RasterizerGLES3() {
time_total = 0;
time_scale = 1;
ShaderGLES3::compiles_started_this_frame = &storage->frame.shader_compiles_started;
}
RasterizerGLES3::~RasterizerGLES3() {

View file

@ -1817,7 +1817,7 @@ void RasterizerSceneGLES3::_setup_light(RenderList::Element *e, const Transform
GIProbeInstance *gipi = gi_probe_instance_owner.getptr(ridp[0]);
float bias_scale = e->instance->baked_light ? 1 : 0;
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 9);
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 10);
glBindTexture(GL_TEXTURE_3D, gipi->tex_cache);
state.scene_shader.set_uniform(SceneShaderGLES3::GI_PROBE_XFORM1, gipi->transform_to_data * p_view_transform);
state.scene_shader.set_uniform(SceneShaderGLES3::GI_PROBE_BOUNDS1, gipi->bounds);
@ -1829,7 +1829,7 @@ void RasterizerSceneGLES3::_setup_light(RenderList::Element *e, const Transform
if (gi_probe_count > 1) {
GIProbeInstance *gipi2 = gi_probe_instance_owner.getptr(ridp[1]);
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 10);
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 11);
glBindTexture(GL_TEXTURE_3D, gipi2->tex_cache);
state.scene_shader.set_uniform(SceneShaderGLES3::GI_PROBE_XFORM2, gipi2->transform_to_data * p_view_transform);
state.scene_shader.set_uniform(SceneShaderGLES3::GI_PROBE_BOUNDS2, gipi2->bounds);
@ -1850,7 +1850,7 @@ void RasterizerSceneGLES3::_setup_light(RenderList::Element *e, const Transform
RasterizerStorageGLES3::LightmapCapture *capture = storage->lightmap_capture_data_owner.getornull(e->instance->lightmap_capture->base);
if (lightmap && capture) {
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 9);
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 10);
if (e->instance->lightmap_slice == -1) {
glBindTexture(GL_TEXTURE_2D, lightmap->tex_id);
} else {
@ -1897,13 +1897,14 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_
glBindBufferBase(GL_UNIFORM_BUFFER, 2, state.env_radiance_ubo); //bind environment radiance info
if (p_sky != nullptr) {
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 2);
if (storage->config.use_texture_array_environment) {
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 3);
glBindTexture(GL_TEXTURE_2D_ARRAY, p_sky->radiance);
} else {
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 2);
glBindTexture(GL_TEXTURE_2D, p_sky->radiance);
}
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 6);
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 7);
glBindTexture(GL_TEXTURE_2D, p_sky->irradiance);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_RADIANCE_MAP, true);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_RADIANCE_MAP_ARRAY, storage->config.use_texture_array_environment);
@ -2572,7 +2573,7 @@ void RasterizerSceneGLES3::_setup_environment(Environment *env, const CameraMatr
state.ubo_data.shadow_directional_pixel_size[0] = 1.0 / directional_shadow.size;
state.ubo_data.shadow_directional_pixel_size[1] = 1.0 / directional_shadow.size;
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 4);
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 5);
glBindTexture(GL_TEXTURE_2D, directional_shadow.depth);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LESS);
@ -3170,7 +3171,7 @@ void RasterizerSceneGLES3::_bind_depth_texture() {
if (!state.bound_depth_texture) {
ERR_FAIL_COND(!state.prepared_depth_texture);
//bind depth for read
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 8);
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 9);
glBindTexture(GL_TEXTURE_2D, storage->frame.current_rt->depth);
state.bound_depth_texture = true;
}
@ -4026,7 +4027,7 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const
state.scene_shader.set_conditional(SceneShaderGLES3::USE_SHADOW, use_shadows);
if (use_shadows) {
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 5);
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 6);
glBindTexture(GL_TEXTURE_2D, shadow_atlas->depth);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LESS);
@ -4035,7 +4036,7 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const
}
if (reflection_atlas && reflection_atlas->size) {
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 3);
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 4);
glBindTexture(GL_TEXTURE_2D, reflection_atlas->color);
}
@ -4464,7 +4465,7 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const
}
if (storage->frame.current_rt && state.used_screen_texture && storage->frame.current_rt->buffers.active) {
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 7);
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 8);
glBindTexture(GL_TEXTURE_2D, storage->frame.current_rt->effects.mip_maps[0].color);
}
@ -5235,6 +5236,10 @@ void RasterizerSceneGLES3::initialize() {
state.debug_draw = VS::VIEWPORT_DEBUG_DRAW_DISABLED;
glFrontFace(GL_CW);
if (storage->config.async_compilation_enabled) {
state.scene_shader.init_async_compilation();
}
}
void RasterizerSceneGLES3::iteration() {

View file

@ -29,10 +29,22 @@
/*************************************************************************/
#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"
#include "servers/visual_server.h"
#if defined(IPHONE_ENABLED) || defined(ANDROID_ENABLED)
#include <dlfcn.h>
#endif
#ifdef TOOLS_ENABLED
#include "editor/editor_settings.h"
#endif
/* TEXTURE API */
@ -2169,6 +2181,8 @@ void RasterizerStorageGLES3::_update_shader(Shader *p_shader) const {
ShaderCompilerGLES3::GeneratedCode gen_code;
ShaderCompilerGLES3::IdentifierActions *actions = nullptr;
int async_mode = (int)ShaderGLES3::ASYNC_MODE_VISIBLE;
switch (p_shader->mode) {
case VS::SHADER_CANVAS_ITEM: {
p_shader->canvas_item.light_mode = Shader::CanvasItem::LIGHT_MODE_NORMAL;
@ -2249,6 +2263,9 @@ void RasterizerStorageGLES3::_update_shader(Shader *p_shader) const {
shaders.actions_scene.render_mode_values["cull_back"] = Pair<int *, int>(&p_shader->spatial.cull_mode, Shader::Spatial::CULL_MODE_BACK);
shaders.actions_scene.render_mode_values["cull_disabled"] = Pair<int *, int>(&p_shader->spatial.cull_mode, Shader::Spatial::CULL_MODE_DISABLED);
shaders.actions_scene.render_mode_values["async_visible"] = Pair<int *, int>(&async_mode, (int)ShaderGLES3::ASYNC_MODE_VISIBLE);
shaders.actions_scene.render_mode_values["async_hidden"] = Pair<int *, int>(&async_mode, (int)ShaderGLES3::ASYNC_MODE_HIDDEN);
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,8 +2310,6 @@ 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->ubo_size = gen_code.uniform_total_size;
p_shader->ubo_offsets = gen_code.uniform_offsets;
p_shader->texture_count = gen_code.texture_uniforms.size();
@ -2317,6 +2332,8 @@ void RasterizerStorageGLES3::_update_shader(Shader *p_shader) const {
}
}
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, (ShaderGLES3::AsyncMode)async_mode);
//all materials using this shader will have to be invalidated, unfortunately
for (SelfList<Material> *E = p_shader->materials.first(); E; E = E->next()) {
@ -2509,6 +2526,14 @@ void RasterizerStorageGLES3::shader_remove_custom_define(RID p_shader, const Str
_shader_make_dirty(shader);
}
void RasterizerStorageGLES3::set_shader_async_hidden_forbidden(bool p_forbidden) {
ShaderGLES3::async_hidden_forbidden = p_forbidden;
}
bool RasterizerStorageGLES3::is_shader_async_hidden_forbidden() {
return ShaderGLES3::async_hidden_forbidden;
}
/* COMMON MATERIAL API */
void RasterizerStorageGLES3::_material_make_dirty(Material *p_material) const {
@ -3407,6 +3432,8 @@ void RasterizerStorageGLES3::mesh_add_surface(RID p_mesh, uint32_t p_format, VS:
attribs[i].size = 2;
attribs[i].type = GL_SHORT;
attributes_stride += 4;
// Storing normal/tangent in the tangent attrib makes it easier to ubershaderify the scene shader
attribs[i].index = VS::ARRAY_TANGENT;
} else {
attribs[i].size = 3;
@ -8061,7 +8088,6 @@ void RasterizerStorageGLES3::initialize() {
config.texture_float_linear_supported = true;
config.framebuffer_float_supported = true;
config.framebuffer_half_float_supported = true;
#else
config.etc2_supported = true;
config.s3tc_supported = config.extensions.has("GL_EXT_texture_compression_dxt1") || config.extensions.has("GL_EXT_texture_compression_s3tc") || config.extensions.has("WEBGL_compressed_texture_s3tc");
@ -8069,7 +8095,6 @@ void RasterizerStorageGLES3::initialize() {
config.texture_float_linear_supported = config.extensions.has("GL_OES_texture_float_linear");
config.framebuffer_float_supported = config.extensions.has("GL_EXT_color_buffer_float");
config.framebuffer_half_float_supported = config.extensions.has("GL_EXT_color_buffer_half_float") || config.framebuffer_float_supported;
#endif
// not yet detected on GLES3 (is this mandated?)
@ -8085,8 +8110,103 @@ 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
#ifdef JAVASCRIPT_ENABLED
config.program_binary_supported = false;
#else
config.program_binary_supported = true;
#endif
config.parallel_shader_compile_supported = config.extensions.has("GL_KHR_parallel_shader_compile") || config.extensions.has("GL_ARB_parallel_shader_compile");
#endif
if (Engine::get_singleton()->is_editor_hint()) {
config.async_compilation_enabled = false;
config.shader_cache_enabled = false;
} else {
int compilation_mode = ProjectSettings::get_singleton()->get("rendering/gles3/shaders/shader_compilation_mode");
config.async_compilation_enabled = compilation_mode >= 1;
config.shader_cache_enabled = compilation_mode == 2;
}
if (config.async_compilation_enabled) {
ShaderGLES3::max_simultaneous_compiles = MAX(1, (int)ProjectSettings::get_singleton()->get("rendering/gles3/shaders/max_simultaneous_compiles"));
#ifdef GLES_OVER_GL
if (GLAD_GL_ARB_parallel_shader_compile) {
glMaxShaderCompilerThreadsARB(ShaderGLES3::max_simultaneous_compiles);
} else if (GLAD_GL_KHR_parallel_shader_compile) {
glMaxShaderCompilerThreadsKHR(ShaderGLES3::max_simultaneous_compiles);
}
#else
#if defined(IPHONE_ENABLED) || defined(ANDROID_ENABLED) // TODO: Consider more platforms?
void *gles3_lib = nullptr;
void (*MaxShaderCompilerThreads)(GLuint) = nullptr;
#if defined(IPHONE_ENABLED)
gles3_lib = dlopen(nullptr, RTLD_LAZY);
#elif defined(ANDROID_ENABLED)
gles3_lib = dlopen("libGLESv3.so", RTLD_LAZY);
#endif
if (gles3_lib) {
MaxShaderCompilerThreads = (void (*)(GLuint))dlsym(gles3_lib, "glMaxShaderCompilerThreadsARB");
if (!MaxShaderCompilerThreads) {
MaxShaderCompilerThreads = (void (*)(GLuint))dlsym(gles3_lib, "glMaxShaderCompilerThreadsKHR");
}
}
if (MaxShaderCompilerThreads) {
MaxShaderCompilerThreads(ShaderGLES3::max_simultaneous_compiles);
} else {
#ifdef DEBUG_ENABLED
print_line("Async. shader compilation: No MaxShaderCompilerThreads function found.");
#endif
}
#endif
#endif
} else {
ShaderGLES3::max_simultaneous_compiles = 0;
}
#ifdef DEBUG_ENABLED
ShaderGLES3::log_active_async_compiles_count = (bool)ProjectSettings::get_singleton()->get("rendering/gles3/shaders/log_active_async_compiles_count");
#endif
frame.clear_request = false;
shaders.compile_queue = nullptr;
shaders.cache = nullptr;
shaders.cache_write_queue = nullptr;
bool effectively_on = false;
if (config.async_compilation_enabled) {
if (config.parallel_shader_compile_supported) {
print_line("Async. shader compilation: ON (full native support)");
effectively_on = true;
} else if (config.program_binary_supported && OS::get_singleton()->is_offscreen_gl_available()) {
shaders.compile_queue = memnew(ThreadedCallableQueue<GLuint>());
shaders.compile_queue->enqueue(0, []() { OS::get_singleton()->set_offscreen_gl_current(true); });
print_line("Async. shader compilation: ON (via secondary context)");
effectively_on = true;
} else {
print_line("Async. shader compilation: OFF (enabled for " + String(Engine::get_singleton()->is_editor_hint() ? "editor" : "project") + ", but not supported)");
}
if (effectively_on) {
if (config.shader_cache_enabled) {
if (config.program_binary_supported) {
print_line("Shader cache: ON");
shaders.cache = memnew(ShaderCacheGLES3);
shaders.cache_write_queue = memnew(ThreadedCallableQueue<GLuint>());
} else {
print_line("Shader cache: OFF (enabled, but not supported)");
}
} else {
print_line("Shader cache: OFF");
}
}
} else {
print_line("Async. shader compilation: OFF");
}
ShaderGLES3::compile_queue = shaders.compile_queue;
ShaderGLES3::parallel_compile_supported = config.parallel_shader_compile_supported;
ShaderGLES3::shader_cache = shaders.cache;
ShaderGLES3::cache_write_queue = shaders.cache_write_queue;
shaders.copy.init();
{
@ -8233,6 +8353,9 @@ void RasterizerStorageGLES3::initialize() {
bool ggx_hq = GLOBAL_GET("rendering/quality/reflections/high_quality_ggx");
shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::LOW_QUALITY, !ggx_hq);
shaders.particles.init();
if (config.async_compilation_enabled) {
shaders.particles.init_async_compilation();
}
#ifdef GLES_OVER_GL
glEnable(_EXT_TEXTURE_CUBE_MAP_SEAMLESS);
@ -8303,3 +8426,16 @@ 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);
}
}

View file

@ -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 K>
class ThreadedCallableQueue;
class RasterizerCanvasGLES3;
class RasterizerSceneGLES3;
@ -113,12 +116,20 @@ public:
// 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<GLuint> *cache_write_queue;
ThreadedCallableQueue<GLuint> *compile_queue;
CubemapFilterShaderGLES3 cubemap_filter;
@ -547,6 +558,9 @@ public:
virtual void shader_get_custom_defines(RID p_shader, Vector<String> *p_defines) const;
virtual void shader_remove_custom_define(RID p_shader, const String &p_define);
virtual void set_shader_async_hidden_forbidden(bool p_forbidden);
virtual bool is_shader_async_hidden_forbidden();
void _update_shader(Shader *p_shader) const;
void update_dirty_shaders();
@ -1476,6 +1490,7 @@ public:
float time[4];
float delta;
uint64_t count;
int shader_compiles_started;
} frame;
@ -1500,6 +1515,7 @@ public:
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 {

View file

@ -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/project_settings.h"
#include "core/sort_array.h"
#include "core/ustring.h"
String ShaderCacheGLES3::hash_program(const char *const *p_strings_platform, const LocalVector<const char *> &p_vertex_strings, const LocalVector<const char *> &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<uint8_t *>(const_cast<char *>(*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<uint64_t>(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<Entry> 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_verbose("Shader cache size: " + itos(total_size / (1024 * 1024)) + " MiB (max. is " + (itos(storage_size / (1024 * 1024))) + " MiB)");
if (total_size > storage_size) {
print_verbose("Purging LRU from shader cache.");
SortArray<Entry>().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 <= storage_size) {
break;
}
}
}
}
ShaderCacheGLES3::ShaderCacheGLES3() {
storage_size = (int)GLOBAL_GET("rendering/gles3/shaders/shader_cache_size_mb") * 1024 * 1024;
storage_da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
storage_path = OS::get_singleton()->get_cache_path().plus_file(OS::get_singleton()->get_godot_dir_name()).plus_file("shaders");
print_verbose("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);
}
}

View file

@ -0,0 +1,58 @@
/*************************************************************************/
/* 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 {
DirAccess *storage_da;
String storage_path;
uint64_t storage_size = 0;
void _purge_excess();
public:
static String hash_program(const char *const *p_platform_strings, const LocalVector<const char *> &p_vertex_strings, const LocalVector<const char *> &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

View file

@ -448,8 +448,8 @@ String ShaderCompilerGLES3::_dump_node_code(const SL::Node *p_node, int p_level,
int max_texture_uniforms = 0;
int max_uniforms = 0;
for (Map<StringName, SL::ShaderNode::Uniform>::Element *E = pnode->uniforms.front(); E; E = E->next()) {
if (SL::is_sampler_type(E->get().type)) {
for (OrderedHashMap<StringName, SL::ShaderNode::Uniform>::Element E = pnode->uniforms.front(); E; E = E.next()) {
if (SL::is_sampler_type(E.get().type)) {
max_texture_uniforms++;
} else {
max_uniforms++;
@ -468,34 +468,34 @@ String ShaderCompilerGLES3::_dump_node_code(const SL::Node *p_node, int p_level,
uniform_defines.resize(max_uniforms);
bool uses_uniforms = false;
for (Map<StringName, SL::ShaderNode::Uniform>::Element *E = pnode->uniforms.front(); E; E = E->next()) {
for (OrderedHashMap<StringName, SL::ShaderNode::Uniform>::Element E = pnode->uniforms.front(); E; E = E.next()) {
String ucode;
if (SL::is_sampler_type(E->get().type)) {
if (SL::is_sampler_type(E.get().type)) {
ucode = "uniform ";
}
ucode += _prestr(E->get().precision);
ucode += _typestr(E->get().type);
ucode += " " + _mkid(E->key());
ucode += _prestr(E.get().precision);
ucode += _typestr(E.get().type);
ucode += " " + _mkid(E.key());
ucode += ";\n";
if (SL::is_sampler_type(E->get().type)) {
if (SL::is_sampler_type(E.get().type)) {
r_gen_code.vertex_global += ucode;
r_gen_code.fragment_global += ucode;
r_gen_code.texture_uniforms.write[E->get().texture_order] = _mkid(E->key());
r_gen_code.texture_hints.write[E->get().texture_order] = E->get().hint;
r_gen_code.texture_types.write[E->get().texture_order] = E->get().type;
r_gen_code.texture_uniforms.write[E.get().texture_order] = _mkid(E.key());
r_gen_code.texture_hints.write[E.get().texture_order] = E.get().hint;
r_gen_code.texture_types.write[E.get().texture_order] = E.get().type;
} else {
if (!uses_uniforms) {
r_gen_code.defines.push_back(String("#define USE_MATERIAL\n").ascii());
uses_uniforms = true;
}
uniform_defines.write[E->get().order] = ucode;
uniform_sizes.write[E->get().order] = _get_datatype_size(E->get().type);
uniform_alignments.write[E->get().order] = _get_datatype_alignment(E->get().type);
uniform_defines.write[E.get().order] = ucode;
uniform_sizes.write[E.get().order] = _get_datatype_size(E.get().type);
uniform_alignments.write[E.get().order] = _get_datatype_alignment(E.get().type);
}
p_actions.uniforms->insert(E->key(), E->get());
p_actions.uniforms->insert(E.key(), E.get());
}
for (int i = 0; i < max_uniforms; i++) {
@ -523,20 +523,20 @@ String ShaderCompilerGLES3::_dump_node_code(const SL::Node *p_node, int p_level,
List<Pair<StringName, SL::ShaderNode::Varying>> var_frag_to_light;
for (Map<StringName, SL::ShaderNode::Varying>::Element *E = pnode->varyings.front(); E; E = E->next()) {
if (E->get().stage == SL::ShaderNode::Varying::STAGE_FRAGMENT_TO_LIGHT || E->get().stage == SL::ShaderNode::Varying::STAGE_FRAGMENT) {
var_frag_to_light.push_back(Pair<StringName, SL::ShaderNode::Varying>(E->key(), E->get()));
fragment_varyings.insert(E->key());
for (OrderedHashMap<StringName, SL::ShaderNode::Varying>::Element E = pnode->varyings.front(); E; E = E.next()) {
if (E.get().stage == SL::ShaderNode::Varying::STAGE_FRAGMENT_TO_LIGHT || E.get().stage == SL::ShaderNode::Varying::STAGE_FRAGMENT) {
var_frag_to_light.push_back(Pair<StringName, SL::ShaderNode::Varying>(E.key(), E.get()));
fragment_varyings.insert(E.key());
continue;
}
String vcode;
String interp_mode = _interpstr(E->get().interpolation);
vcode += _prestr(E->get().precision);
vcode += _typestr(E->get().type);
vcode += " " + _mkid(E->key());
if (E->get().array_size > 0) {
String interp_mode = _interpstr(E.get().interpolation);
vcode += _prestr(E.get().precision);
vcode += _typestr(E.get().type);
vcode += " " + _mkid(E.key());
if (E.get().array_size > 0) {
vcode += "[";
vcode += itos(E->get().array_size);
vcode += itos(E.get().array_size);
vcode += "]";
}
vcode += ";\n";

File diff suppressed because it is too large Load diff

View file

@ -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 <stdio.h>
template <class K>
class ThreadedCallableQueue;
class ShaderCacheGLES3;
class ShaderGLES3 {
protected:
struct Enum {
@ -83,7 +90,7 @@ protected:
int conditional;
};
bool uniforms_dirty;
virtual int get_ubershader_flags_uniform() const { return -1; }
private:
//@TODO Optimize to a fixed set of shader pools and use a LRU
@ -96,6 +103,13 @@ private:
int fragment_code_start;
int attribute_pair_count;
public:
enum AsyncMode {
ASYNC_MODE_VISIBLE,
ASYNC_MODE_HIDDEN,
};
private:
struct CustomCode {
String vertex;
String vertex_globals;
@ -103,32 +117,35 @@ private:
String fragment_globals;
String light;
String uniforms;
AsyncMode async_mode;
uint32_t version;
Vector<StringName> texture_uniforms;
Vector<CharString> custom_defines;
Set<uint32_t> versions;
};
struct Version {
GLuint id;
GLuint vert_id;
GLuint frag_id;
GLint *uniform_location;
Vector<GLint> texture_uniform_locations;
uint32_t code_version;
bool ok;
Version() :
id(0),
vert_id(0),
frag_id(0),
uniform_location(nullptr),
code_version(0),
ok(false) {}
};
public:
static ShaderCacheGLES3 *shader_cache;
static ThreadedCallableQueue<GLuint> *cache_write_queue;
Version *version;
static ThreadedCallableQueue<GLuint> *compile_queue; // Non-null if using queued asynchronous compilation (via seconday context)
static bool parallel_compile_supported; // True if using natively supported asyncrhonous compilation
static bool async_hidden_forbidden;
static int *compiles_started_this_frame;
static int max_simultaneous_compiles;
#ifdef DEBUG_ENABLED
static bool log_active_async_compiles_count;
#endif
static uint64_t current_frame;
static void advance_async_shaders_compilation();
private:
static int active_compiles_count;
union VersionKey {
static const uint32_t UBERSHADER_FLAG = ((uint32_t)1) << 31;
struct {
uint32_t version;
uint32_t code_version;
@ -136,8 +153,79 @@ private:
uint64_t key;
bool operator==(const VersionKey &p_key) const { return key == p_key.key; }
bool operator<(const VersionKey &p_key) const { return key < p_key.key; }
VersionKey() {}
VersionKey(uint64_t p_key) :
key(p_key) {}
_FORCE_INLINE_ bool is_subject_to_caching() const { return (version & UBERSHADER_FLAG); }
};
struct Version {
VersionKey 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;
AsyncMode async_mode;
GLint *uniform_location;
Vector<GLint> texture_uniform_locations;
bool uniforms_ready;
uint64_t last_frame_processed;
enum CompileStatus {
COMPILE_STATUS_PENDING,
COMPILE_STATUS_SOURCE_PROVIDED,
COMPILE_STATUS_COMPILING_VERTEX,
COMPILE_STATUS_COMPILING_FRAGMENT,
COMPILE_STATUS_COMPILING_VERTEX_AND_FRAGMENT,
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<Version> 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<int> result_from_queue;
} program_binary;
Version() :
version_key(0),
ids(),
shader(nullptr),
code_version(0),
async_mode(ASYNC_MODE_VISIBLE),
uniform_location(nullptr),
uniforms_ready(false),
last_frame_processed(UINT64_MAX),
compile_status(COMPILE_STATUS_PENDING),
compiling_list(this),
program_binary() {}
};
static SelfList<Version>::List versions_compiling;
Version *version;
struct VersionKeyHash {
static _FORCE_INLINE_ uint32_t hash(const VersionKey &p_key) { return HashMapHasherDefault::hash(p_key.key); };
};
@ -176,7 +264,16 @@ private:
int base_material_tex_index;
Version *get_current_version();
Version *get_current_version(bool &r_async_forbidden);
// 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<const char *> &p_vertex_strings, const LocalVector<const char *> &p_fragment_strings) 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;
// ---
static void _log_active_compiles();
static bool _process_program_state(Version *p_version, bool p_async_forbidden);
void _setup_uniforms(CustomCode *p_cc) const;
void _dispose_program(Version *p_version);
static ShaderGLES3 *active;
@ -271,8 +368,8 @@ private:
}
}
Map<uint32_t, Variant> uniform_defaults;
Map<uint32_t, CameraMatrix> uniform_cameras;
bool _bind(bool p_binding_fallback);
bool _bind_ubershader();
protected:
_FORCE_INLINE_ int _get_uniform(int p_which) const;
@ -293,47 +390,20 @@ public:
static _FORCE_INLINE_ ShaderGLES3 *get_active() { return active; };
bool bind();
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<StringName> &p_texture_uniforms, const Vector<CharString> &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<StringName> &p_texture_uniforms, const Vector<CharString> &p_custom_defines, AsyncMode p_async_mode);
void set_custom_shader(uint32_t p_code_id);
void free_custom_shader(uint32_t p_code_id);
void set_uniform_default(int p_idx, const Variant &p_value) {
if (p_value.get_type() == Variant::NIL) {
uniform_defaults.erase(p_idx);
} else {
uniform_defaults[p_idx] = p_value;
}
uniforms_dirty = true;
}
uint32_t get_version() const { return new_conditional_version.version; }
_FORCE_INLINE_ bool is_version_valid() const { return version && version->ok; }
void set_uniform_camera(int p_idx, const CameraMatrix &p_mat) {
uniform_cameras[p_idx] = p_mat;
uniforms_dirty = true;
};
_FORCE_INLINE_ void set_texture_uniform(int p_idx, const Variant &p_value) {
ERR_FAIL_COND(!version);
ERR_FAIL_INDEX(p_idx, version->texture_uniform_locations.size());
_set_uniform_variant(version->texture_uniform_locations[p_idx], p_value);
}
_FORCE_INLINE_ GLint get_texture_uniform_location(int p_idx) {
ERR_FAIL_COND_V(!version, -1);
ERR_FAIL_INDEX_V(p_idx, version->texture_uniform_locations.size(), -1);
return version->texture_uniform_locations[p_idx];
}
_FORCE_INLINE_ bool is_version_valid() const { return version && version->compile_status == Version::COMPILE_STATUS_OK; }
virtual void init() = 0;
void init_async_compilation();
bool is_async_compilation_supported();
void finish();
void set_base_material_tex_index(int p_idx);

View file

@ -1,6 +1,10 @@
/* clang-format off */
[vertex]
#if defined(IS_UBERSHADER)
uniform highp int ubershader_flags;
#endif
layout(location = 0) in highp vec4 color;
/* clang-format on */
layout(location = 1) in highp vec4 velocity_active;
@ -70,17 +74,6 @@ uint hash(uint x) {
}
void main() {
#ifdef PARTICLES_COPY
out_color = color;
out_velocity_active = velocity_active;
out_custom = custom;
out_xform_1 = xform_1;
out_xform_2 = xform_2;
out_xform_3 = xform_3;
#else
bool apply_forces = true;
bool apply_velocity = true;
float local_delta = delta;
@ -109,22 +102,22 @@ void main() {
if (restart_phase >= prev_system_phase && restart_phase < system_phase) {
restart = true;
#ifdef USE_FRACTIONAL_DELTA
#ifdef USE_FRACTIONAL_DELTA //ubershader-runtime
local_delta = (system_phase - restart_phase) * lifetime;
#endif
#endif //ubershader-runtime
}
} else if (delta > 0.0) {
if (restart_phase >= prev_system_phase) {
restart = true;
#ifdef USE_FRACTIONAL_DELTA
#ifdef USE_FRACTIONAL_DELTA //ubershader-runtime
local_delta = (1.0 - restart_phase + system_phase) * lifetime;
#endif
#endif //ubershader-runtime
} else if (restart_phase < system_phase) {
restart = true;
#ifdef USE_FRACTIONAL_DELTA
#ifdef USE_FRACTIONAL_DELTA //ubershader-runtime
local_delta = (system_phase - restart_phase) * lifetime;
#endif
#endif //ubershader-runtime
}
}
@ -223,13 +216,15 @@ VERTEX_SHADER_CODE
out_xform_1 = xform[0];
out_xform_2 = xform[1];
out_xform_3 = xform[2];
#endif //PARTICLES_COPY
}
/* clang-format off */
[fragment]
#if defined(IS_UBERSHADER)
uniform highp int ubershader_flags;
#endif
// any code here is never executed, stuff is filled just so it works
#if defined(USE_MATERIAL)

File diff suppressed because it is too large Load diff

View file

@ -268,6 +268,10 @@ void EditorExportPlatform::gen_debug_flags(Vector<String> &r_flags, int p_flags)
if (p_flags & DEBUG_FLAG_VIEW_NAVIGATION) {
r_flags.push_back("--debug-navigation");
}
if (p_flags & DEBUG_FLAG_SHADER_FALLBACKS) {
r_flags.push_back("--debug-shader-fallbacks");
}
}
Error EditorExportPlatform::_save_pack_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total) {

View file

@ -246,6 +246,7 @@ public:
DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST = 4,
DEBUG_FLAG_VIEW_COLLISONS = 8,
DEBUG_FLAG_VIEW_NAVIGATION = 16,
DEBUG_FLAG_SHADER_FALLBACKS = 32,
};
virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) { return OK; }

View file

@ -2748,6 +2748,13 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
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_shader_fallbacks(!ischecked);
editor_run.set_debug_shader_fallbacks(!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));
debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(RUN_RELOAD_SCRIPTS), !ischecked);
@ -3004,6 +3011,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);
@ -3019,6 +3027,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);
}
@ -6342,32 +6353,50 @@ EditorNode::EditorNode() {
p = debug_menu->get_popup();
p->set_hide_on_window_lose_focus(true);
p->set_hide_on_checkable_item_selection(false);
p->add_check_shortcut(ED_SHORTCUT("editor/deploy_with_remote_debug", TTR("Deploy with Remote Debug")), RUN_DEPLOY_REMOTE_DEBUG);
p->set_item_tooltip(
p->get_item_count() - 1,
TTR("When this option is enabled, using one-click deploy will make the executable attempt to connect to this computer's IP so the running project can be debugged.\nThis option is intended to be used for remote debugging (typically with a mobile device).\nYou don't need to enable it to use the GDScript debugger locally."));
p->add_check_shortcut(ED_SHORTCUT("editor/small_deploy_with_network_fs", TTR("Small Deploy with Network Filesystem")), RUN_FILE_SERVER);
p->set_item_tooltip(
p->get_item_count() - 1,
TTR("When this option is enabled, using one-click deploy for Android will only export an executable without the project data.\nThe filesystem will be provided from the project by the editor over the network.\nOn Android, deploying will use the USB cable for faster performance. This option speeds up testing for projects with large assets."));
p->add_separator();
p->add_check_shortcut(ED_SHORTCUT("editor/visible_collision_shapes", TTR("Visible Collision Shapes")), RUN_DEBUG_COLLISONS);
p->set_item_tooltip(
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, navigation meshes and polygons will be visible in the running project."));
if (GLOBAL_GET("rendering/quality/driver/driver_name") == "GLES3") {
p->add_separator();
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, shaders will be used in their fallback form (either visible via an ubershader or hidden) during all the run time.\nThis is useful for verifying the look and performance of fallbacks, which are normally displayed briefly.\nAsynchronous shader compilation must be enabled in the project settings for this option to make a difference."));
}
p->add_separator();
p->add_check_shortcut(ED_SHORTCUT("editor/sync_scene_changes", TTR("Synchronize Scene Changes")), RUN_LIVE_DEBUG);
p->set_item_tooltip(
p->get_item_count() - 1,
TTR("When this option is enabled, any changes made to the scene in the editor will be replicated in the running project.\nWhen used remotely on a device, this is more efficient when the network filesystem option is enabled."));
p->add_check_shortcut(ED_SHORTCUT("editor/sync_script_changes", TTR("Synchronize Script Changes")), RUN_RELOAD_SCRIPTS);
p->set_item_tooltip(
p->get_item_count() - 1,
TTR("When this option is enabled, any script that is saved will be reloaded in the running project.\nWhen used remotely on a device, this is more efficient when the network filesystem option is enabled."));
p->connect("id_pressed", this, "_menu_option");
menu_hb->add_spacer();

View file

@ -170,6 +170,7 @@ private:
RUN_LIVE_DEBUG,
RUN_DEBUG_COLLISONS,
RUN_DEBUG_NAVIGATION,
RUN_DEBUG_SHADER_FALLBACKS,
RUN_DEPLOY_REMOTE_DEBUG,
RUN_RELOAD_SCRIPTS,
RUN_VCS_SETTINGS,

View file

@ -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_shader_fallbacks) {
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,18 @@ bool EditorRun::get_debug_navigation() const {
return debug_navigation;
}
void EditorRun::set_debug_shader_fallbacks(bool p_debug) {
debug_shader_fallbacks = p_debug;
}
bool EditorRun::get_debug_shader_fallbacks() const {
return debug_shader_fallbacks;
}
EditorRun::EditorRun() {
status = STATUS_STOP;
running_scene = "";
debug_collisions = false;
debug_navigation = false;
debug_shader_fallbacks = false;
}

View file

@ -47,6 +47,7 @@ public:
private:
bool debug_collisions;
bool debug_navigation;
bool debug_shader_fallbacks;
Status status;
String running_scene;
@ -65,6 +66,9 @@ public:
void set_debug_navigation(bool p_debug);
bool get_debug_navigation() const;
void set_debug_shader_fallbacks(bool p_debug);
bool get_debug_shader_fallbacks() const;
EditorRun();
};

View file

@ -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_shader_fallbacks) {
flags |= EditorExportPlatform::DEBUG_FLAG_SHADER_FALLBACKS;
}
eep->run(preset, p_idx, flags);
}
@ -188,6 +191,14 @@ bool EditorRunNative::get_debug_navigation() const {
return debug_navigation;
}
void EditorRunNative::set_debug_shader_fallbacks(bool p_debug) {
debug_shader_fallbacks = p_debug;
}
bool EditorRunNative::get_debug_shader_fallbacks() const {
return debug_shader_fallbacks;
}
EditorRunNative::EditorRunNative() {
set_process(true);
first = true;
@ -195,6 +206,7 @@ EditorRunNative::EditorRunNative() {
deploy_debug_remote = false;
debug_collisions = false;
debug_navigation = false;
debug_shader_fallbacks = false;
resume_idx = 0;
resume_platform = 0;
}

View file

@ -43,6 +43,7 @@ class EditorRunNative : public HBoxContainer {
bool deploy_debug_remote;
bool debug_collisions;
bool debug_navigation;
bool debug_shader_fallbacks;
int resume_idx;
int resume_platform;
@ -66,6 +67,9 @@ public:
void set_debug_navigation(bool p_debug);
bool get_debug_navigation() const;
void set_debug_shader_fallbacks(bool p_debug);
bool get_debug_shader_fallbacks() const;
void resume_run_native();
EditorRunNative();

View file

@ -4,6 +4,7 @@ All such functions are invoked in a subprocess on Windows to prevent build flaki
"""
from platform_methods import subprocess_main
import re
class LegacyGLHeaderStruct:
@ -69,7 +70,7 @@ def include_file_in_legacygl_header(filename, header_data, depth):
if line.find("#ifdef ") != -1:
if line.find("#ifdef ") != -1:
ifdefline = line.replace("#ifdef ", "").strip()
ifdefline = re.sub(r".*#ifdef (\S+).*\n", "\\1", line)
if line.find("_EN_") != -1:
enumbase = ifdefline[: ifdefline.find("_EN_")]
@ -227,6 +228,10 @@ def build_legacygl_header(filename, include, class_suffix, output_attribs, gles2
fd.write("\t\t" + x.upper() + ",\n")
fd.write("\t};\n\n")
supports_ubershader = not gles2 and "ubershader_flags" in header_data.uniforms
if supports_ubershader:
fd.write("\tint get_ubershader_flags_uniform() const { return Uniforms::UBERSHADER_FLAGS; }\n\n")
fd.write("\t_FORCE_INLINE_ int get_uniform(Uniforms p_uniform) const { return _get_uniform(p_uniform); }\n\n")
if header_data.conditionals:
fd.write(

View file

@ -145,6 +145,7 @@ static bool use_debug_profiler = false;
#ifdef DEBUG_ENABLED
static bool debug_collisions = false;
static bool debug_navigation = false;
static bool debug_shader_fallbacks = false;
#endif
static int frame_delay = 0;
static bool disable_render_loop = false;
@ -292,6 +293,7 @@ void Main::print_help(const char *p_binary) {
#if defined(DEBUG_ENABLED) && !defined(SERVER_ENABLED)
OS::get_singleton()->print(" --debug-collisions Show collision shapes when running the scene.\n");
OS::get_singleton()->print(" --debug-navigation Show navigation polygons when running the scene.\n");
OS::get_singleton()->print(" --debug-shader-fallbacks Use the fallbacks of the shaders which have one when running the scene (GL ES 3 only).\n");
#endif
OS::get_singleton()->print(" --frame-delay <ms> Simulate high CPU load (delay each frame by <ms> milliseconds).\n");
OS::get_singleton()->print(" --time-scale <scale> Force time scale (higher values are faster, 1.0 is normal speed).\n");
@ -806,6 +808,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
debug_collisions = true;
} else if (I->get() == "--debug-navigation") {
debug_navigation = true;
} else if (I->get() == "--debug-shader-fallbacks") {
debug_shader_fallbacks = true;
#endif
} else if (I->get() == "--remote-debug") {
if (I->next()) {
@ -1218,6 +1222,13 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
Engine::get_singleton()->set_frame_delay(frame_delay);
#ifdef DEBUG_ENABLED
if (!Engine::get_singleton()->is_editor_hint()) {
GLOBAL_DEF("rendering/gles3/shaders/debug_shader_fallbacks", debug_shader_fallbacks);
ProjectSettings::get_singleton()->set_hide_from_editor("rendering/gles3/shaders/debug_shader_fallbacks", true);
}
#endif
message_queue = memnew(MessageQueue);
if (p_second_phase) {

View file

@ -120,14 +120,14 @@ static String dump_node_code(SL::Node *p_node, int p_level) {
case SL::Node::TYPE_SHADER: {
SL::ShaderNode *pnode = (SL::ShaderNode *)p_node;
for (Map<StringName, SL::ShaderNode::Uniform>::Element *E = pnode->uniforms.front(); E; E = E->next()) {
for (OrderedHashMap<StringName, SL::ShaderNode::Uniform>::Element E = pnode->uniforms.front(); E; E = E.next()) {
String ucode = "uniform ";
ucode += _prestr(E->get().precision);
ucode += _typestr(E->get().type);
ucode += " " + String(E->key());
ucode += _prestr(E.get().precision);
ucode += _typestr(E.get().type);
ucode += " " + String(E.key());
if (E->get().default_value.size()) {
ucode += " = " + get_constant_text(E->get().type, E->get().default_value);
if (E.get().default_value.size()) {
ucode += " = " + get_constant_text(E.get().type, E.get().default_value);
}
static const char *hint_name[SL::ShaderNode::Uniform::HINT_MAX] = {
@ -140,18 +140,18 @@ static String dump_node_code(SL::Node *p_node, int p_level) {
"white"
};
if (E->get().hint) {
ucode += " : " + String(hint_name[E->get().hint]);
if (E.get().hint) {
ucode += " : " + String(hint_name[E.get().hint]);
}
code += ucode + "\n";
}
for (Map<StringName, SL::ShaderNode::Varying>::Element *E = pnode->varyings.front(); E; E = E->next()) {
for (OrderedHashMap<StringName, SL::ShaderNode::Varying>::Element E = pnode->varyings.front(); E; E = E.next()) {
String vcode = "varying ";
vcode += _prestr(E->get().precision);
vcode += _typestr(E->get().type);
vcode += " " + String(E->key());
vcode += _prestr(E.get().precision);
vcode += _typestr(E.get().type);
vcode += " " + String(E.key());
code += vcode + "\n";
}

View file

@ -1047,6 +1047,17 @@ void SpatialMaterial::_update_shader() {
code += "}\n";
String fallback_mode_str;
switch (async_mode) {
case ASYNC_MODE_VISIBLE: {
fallback_mode_str = "async_visible";
} break;
case ASYNC_MODE_HIDDEN: {
fallback_mode_str = "async_hidden";
} 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 +1833,16 @@ Shader::Mode SpatialMaterial::get_shader_mode() const {
return Shader::MODE_SPATIAL;
}
void SpatialMaterial::set_async_mode(AsyncMode p_mode) {
async_mode = p_mode;
_queue_shader_change();
_change_notify();
}
SpatialMaterial::AsyncMode SpatialMaterial::get_async_mode() const {
return async_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 +2015,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_async_mode", "mode"), &SpatialMaterial::set_async_mode);
ClassDB::bind_method(D_METHOD("get_async_mode"), &SpatialMaterial::get_async_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 +2160,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, "async_mode", PROPERTY_HINT_ENUM, "Visible,Hidden"), "set_async_mode", "get_async_mode");
BIND_ENUM_CONSTANT(TEXTURE_ALBEDO);
BIND_ENUM_CONSTANT(TEXTURE_METALLIC);
BIND_ENUM_CONSTANT(TEXTURE_ROUGHNESS);
@ -2236,6 +2262,9 @@ 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(ASYNC_MODE_VISIBLE);
BIND_ENUM_CONSTANT(ASYNC_MODE_HIDDEN);
}
SpatialMaterial::SpatialMaterial() :
@ -2309,6 +2338,8 @@ SpatialMaterial::SpatialMaterial() :
diffuse_mode = DIFFUSE_BURLEY;
specular_mode = SPECULAR_SCHLICK_GGX;
async_mode = ASYNC_MODE_VISIBLE;
for (int i = 0; i < FEATURE_MAX; i++) {
features[i] = false;
}

View file

@ -237,6 +237,11 @@ public:
DISTANCE_FADE_OBJECT_DITHER,
};
enum AsyncMode {
ASYNC_MODE_VISIBLE,
ASYNC_MODE_HIDDEN,
};
private:
union MaterialKey {
struct {
@ -425,6 +430,7 @@ private:
DiffuseMode diffuse_mode;
BillboardMode billboard_mode;
EmissionOperator emission_op;
AsyncMode async_mode;
TextureChannel metallic_texture_channel;
TextureChannel roughness_texture_channel;
@ -622,6 +628,9 @@ public:
void set_refraction_texture_channel(TextureChannel p_channel);
TextureChannel get_refraction_texture_channel() const;
void set_async_mode(AsyncMode p_mode);
AsyncMode get_async_mode() const;
static void init_shaders();
static void finish_shaders();
static void flush_changes();
@ -649,6 +658,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::AsyncMode)
//////////////////////

View file

@ -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, "async" },
{ Shader::MODE_CANVAS_ITEM, "blend" },
{ Shader::MODE_CANVAS_ITEM, nullptr }
};

View file

@ -248,6 +248,9 @@ public:
virtual void shader_get_custom_defines(RID p_shader, Vector<String> *p_defines) const = 0;
virtual void shader_remove_custom_define(RID p_shader, const String &p_define) = 0;
virtual void set_shader_async_hidden_forbidden(bool p_forbidden) = 0;
virtual bool is_shader_async_hidden_forbidden() = 0;
/* COMMON MATERIAL API */
virtual RID material_create() = 0;

View file

@ -6893,11 +6893,11 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct
}
}
for (const Map<StringName, ShaderNode::Varying>::Element *E = shader->varyings.front(); E; E = E->next()) {
matches.insert(E->key(), ScriptCodeCompletionOption::KIND_VARIABLE);
for (OrderedHashMap<StringName, ShaderNode::Varying>::Element E = shader->varyings.front(); E; E = E.next()) {
matches.insert(E.key(), ScriptCodeCompletionOption::KIND_VARIABLE);
}
for (const Map<StringName, ShaderNode::Uniform>::Element *E = shader->uniforms.front(); E; E = E->next()) {
matches.insert(E->key(), ScriptCodeCompletionOption::KIND_MEMBER);
for (OrderedHashMap<StringName, ShaderNode::Uniform>::Element E = shader->uniforms.front(); E; E = E.next()) {
matches.insert(E.key(), ScriptCodeCompletionOption::KIND_MEMBER);
}
}

View file

@ -33,6 +33,7 @@
#include "core/list.h"
#include "core/map.h"
#include "core/ordered_hash_map.h"
#include "core/script_language.h"
#include "core/string_name.h"
#include "core/typedefs.h"
@ -652,8 +653,8 @@ public:
};
Map<StringName, Constant> constants;
Map<StringName, Varying> varyings;
Map<StringName, Uniform> uniforms;
OrderedHashMap<StringName, Varying> varyings;
OrderedHashMap<StringName, Uniform> uniforms;
Map<StringName, Struct> structs;
Vector<StringName> render_modes;

View file

@ -197,6 +197,9 @@ ShaderTypes::ShaderTypes() {
shader_modes[VS::SHADER_SPATIAL].modes.push_back("vertex_lighting");
shader_modes[VS::SHADER_SPATIAL].modes.push_back("async_visible");
shader_modes[VS::SHADER_SPATIAL].modes.push_back("async_hidden");
/************ CANVAS ITEM **************************/
shader_modes[VS::SHADER_CANVAS_ITEM].functions["global"].built_ins["TIME"] = constt(ShaderLanguage::TYPE_FLOAT);

View file

@ -192,6 +192,8 @@ public:
BIND2C(shader_get_custom_defines, RID, Vector<String> *)
BIND2(shader_remove_custom_define, RID, const String &)
BIND1(set_shader_async_hidden_forbidden, bool)
/* COMMON MATERIAL API */
BIND0R(RID, material_create)

View file

@ -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 async_forbidden_backup = VSG::storage->is_shader_async_hidden_forbidden();
VSG::storage->set_shader_async_hidden_forbidden(true);
_render_scene(xform, cm, 0, false, RID(), p_instance->scenario->self, shadow_atlas, reflection_probe->instance, p_step);
VSG::storage->set_shader_async_hidden_forbidden(async_forbidden_backup);
} else {
//do roughness postprocess step until it believes it's done

View file

@ -130,6 +130,8 @@ public:
FUNC2SC(shader_get_custom_defines, RID, Vector<String> *)
FUNC2(shader_remove_custom_define, RID, const String &)
FUNC1(set_shader_async_hidden_forbidden, bool)
/* COMMON MATERIAL API */
FUNCRID(material)

View file

@ -30,9 +30,14 @@
#include "visual_server.h"
#include "core/engine.h"
#include "core/method_bind_ext.gen.inc"
#include "core/project_settings.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_settings.h"
#endif
VisualServer *VisualServer::singleton = nullptr;
VisualServer *(*VisualServer::create_func)() = nullptr;
@ -1868,6 +1873,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_shader_async_hidden_forbidden", "forbidden"), &VisualServer::set_shader_async_hidden_forbidden);
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);
@ -2582,6 +2588,16 @@ void VisualServer::set_render_loop_enabled(bool p_enabled) {
render_loop_enabled = p_enabled;
}
#ifdef DEBUG_ENABLED
bool VisualServer::is_force_shader_fallbacks_enabled() const {
return force_shader_fallbacks;
}
void VisualServer::set_force_shader_fallbacks_enabled(bool p_enabled) {
force_shader_fallbacks = p_enabled;
}
#endif
VisualServer::VisualServer() {
//ERR_FAIL_COND(singleton);
singleton = this;
@ -2701,6 +2717,22 @@ VisualServer::VisualServer() {
// Occlusion culling
GLOBAL_DEF("rendering/misc/occlusion_culling/max_active_spheres", 8);
ProjectSettings::get_singleton()->set_custom_property_info("rendering/misc/occlusion_culling/max_active_spheres", PropertyInfo(Variant::INT, "rendering/misc/occlusion_culling/max_active_spheres", PROPERTY_HINT_RANGE, "0,64"));
// Async. compilation and caching
#ifdef DEBUG_ENABLED
if (!Engine::get_singleton()->is_editor_hint()) {
force_shader_fallbacks = GLOBAL_GET("rendering/gles3/shaders/debug_shader_fallbacks");
}
#endif
GLOBAL_DEF("rendering/gles3/shaders/shader_compilation_mode", 0);
ProjectSettings::get_singleton()->set_custom_property_info("rendering/gles3/shaders/shader_compilation_mode", PropertyInfo(Variant::INT, "rendering/gles3/shaders/shader_compilation_mode", PROPERTY_HINT_ENUM, "Synchronous,Asynchronous,Asynchronous + Cache"));
GLOBAL_DEF("rendering/gles3/shaders/max_simultaneous_compiles", 2);
ProjectSettings::get_singleton()->set_custom_property_info("rendering/gles3/shaders/max_simultaneous_compiles", PropertyInfo(Variant::INT, "rendering/gles3/shaders/max_simultaneous_compiles", PROPERTY_HINT_RANGE, "1,8,1"));
GLOBAL_DEF("rendering/gles3/shaders/max_simultaneous_compiles.mobile", 1);
GLOBAL_DEF("rendering/gles3/shaders/log_active_async_compiles_count", false);
GLOBAL_DEF("rendering/gles3/shaders/shader_cache_size_mb", 512);
ProjectSettings::get_singleton()->set_custom_property_info("rendering/gles3/shaders/shader_cache_size_mb", PropertyInfo(Variant::INT, "rendering/gles3/shaders/shader_cache_size_mb", PROPERTY_HINT_RANGE, "128,4096,128"));
GLOBAL_DEF("rendering/gles3/shaders/shader_cache_size_mb.mobile", 128);
}
VisualServer::~VisualServer() {

View file

@ -48,6 +48,9 @@ class VisualServer : public Object {
int mm_policy;
bool render_loop_enabled = true;
#ifdef DEBUG_ENABLED
bool force_shader_fallbacks = false;
#endif
void _camera_set_orthogonal(RID p_camera, float p_size, float p_z_near, float p_z_far);
void _canvas_item_add_style_box(RID p_item, const Rect2 &p_rect, const Rect2 &p_source, RID p_texture, const Vector<float> &p_margins, const Color &p_modulate = Color(1, 1, 1));
@ -204,6 +207,8 @@ public:
virtual void shader_get_custom_defines(RID p_shader, Vector<String> *p_defines) const = 0;
virtual void shader_remove_custom_define(RID p_shader, const String &p_define) = 0;
virtual void set_shader_async_hidden_forbidden(bool p_forbidden) = 0;
/* COMMON MATERIAL API */
enum {
@ -1157,6 +1162,11 @@ public:
bool is_render_loop_enabled() const;
void set_render_loop_enabled(bool p_enabled);
#ifdef DEBUG_ENABLED
bool is_force_shader_fallbacks_enabled() const;
void set_force_shader_fallbacks_enabled(bool p_enabled);
#endif
VisualServer();
virtual ~VisualServer();
};

View file

@ -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 <stdio.h>
@ -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;
}

View file

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