diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml
index 5710e084230..7651b61c5be 100644
--- a/doc/classes/CanvasItem.xml
+++ b/doc/classes/CanvasItem.xml
@@ -461,6 +461,13 @@
Returns the transform from the local coordinate system of this [CanvasItem] to the [Viewport]s coordinate system.
+
+
+
+
+ Get the value of a shader parameter as set on this instance.
+
+
@@ -562,6 +569,16 @@
Queues the [CanvasItem] to redraw. During idle time, if [CanvasItem] is visible, [constant NOTIFICATION_DRAW] is sent and [method _draw] is called. This only occurs [b]once[/b] per frame, even if this method has been called multiple times.
+
+
+
+
+
+ Set the value of a shader uniform for this instance only ([url=$DOCS_URL/tutorials/shaders/shader_reference/shading_language.html#per-instance-uniforms]per-instance uniform[/url]). See also [method ShaderMaterial.set_shader_parameter] to assign a uniform on all instances using the same [ShaderMaterial].
+ [b]Note:[/b] For a shader uniform to be assignable on a per-instance basis, it [i]must[/i] be defined with [code]instance uniform ...[/code] rather than [code]uniform ...[/code] in the shader code.
+ [b]Note:[/b] [param name] is case-sensitive and must match the name of the uniform in the code exactly (not the capitalized name in the inspector).
+
+
diff --git a/doc/classes/GeometryInstance3D.xml b/doc/classes/GeometryInstance3D.xml
index 0c45154c8af..9cd7533aeb4 100644
--- a/doc/classes/GeometryInstance3D.xml
+++ b/doc/classes/GeometryInstance3D.xml
@@ -25,7 +25,7 @@
Set the value of a shader uniform for this instance only ([url=$DOCS_URL/tutorials/shaders/shader_reference/shading_language.html#per-instance-uniforms]per-instance uniform[/url]). See also [method ShaderMaterial.set_shader_parameter] to assign a uniform on all instances using the same [ShaderMaterial].
[b]Note:[/b] For a shader uniform to be assignable on a per-instance basis, it [i]must[/i] be defined with [code]instance uniform ...[/code] rather than [code]uniform ...[/code] in the shader code.
[b]Note:[/b] [param name] is case-sensitive and must match the name of the uniform in the code exactly (not the capitalized name in the inspector).
- [b]Note:[/b] Per-instance shader uniforms are currently only available in 3D, so there is no 2D equivalent of this method.
+ [b]Note:[/b] Per-instance shader uniforms are only available in Spatial and CanvasItem shaders, but not for Fog, Sky, or Particles shaders.
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index 6593d69a20f..4669ff448d1 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -436,6 +436,30 @@
[b]Note:[/b] The equivalent node is [CanvasItem].
+
+
+
+
+
+ Returns the value of the per-instance shader uniform from the specified canvas item instance. Equivalent to [method CanvasItem.get_instance_shader_parameter].
+
+
+
+
+
+
+
+ Returns the default value of the per-instance shader uniform from the specified canvas item instance. Equivalent to [method CanvasItem.get_instance_shader_parameter].
+
+
+
+
+
+
+ Returns a dictionary of per-instance shader uniform names of the per-instance shader uniform from the specified canvas item instance.
+ The returned dictionary is in PropertyInfo format, with the keys [code]name[/code], [code]class_name[/code], [code]type[/code], [code]hint[/code], [code]hint_string[/code], and [code]usage[/code].
+
+
@@ -524,6 +548,15 @@
Sets the index for the [CanvasItem].
+
+
+
+
+
+
+ Sets the per-instance shader uniform on the specified canvas item instance. Equivalent to [method CanvasItem.set_instance_shader_parameter].
+
+
diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp
index 5fd90744a42..3905f7a6cb2 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.cpp
+++ b/drivers/gles3/rasterizer_canvas_gles3.cpp
@@ -727,6 +727,7 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
// Bind per-batch uniforms.
material_storage->shaders.canvas_shader.version_set_uniform(CanvasShaderGLES3::BATCH_FLAGS, state.canvas_instance_batches[i].flags, shader_version, variant, specialization);
+ material_storage->shaders.canvas_shader.version_set_uniform(CanvasShaderGLES3::SPECULAR_SHININESS_IN, state.canvas_instance_batches[i].specular_shininess, shader_version, variant, specialization);
GLES3::CanvasShaderData::BlendMode blend_mode = state.canvas_instance_batches[i].blend_mode;
Color blend_color = state.canvas_instance_batches[i].blend_color;
@@ -907,15 +908,15 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
state.instance_data_array[r_index].color_texture_pixel_size[0] = 0.0;
state.instance_data_array[r_index].color_texture_pixel_size[1] = 0.0;
- state.instance_data_array[r_index].pad[0] = 0.0;
- state.instance_data_array[r_index].pad[1] = 0.0;
-
state.instance_data_array[r_index].lights[0] = lights[0];
state.instance_data_array[r_index].lights[1] = lights[1];
state.instance_data_array[r_index].lights[2] = lights[2];
state.instance_data_array[r_index].lights[3] = lights[3];
state.instance_data_array[r_index].flags = base_flags;
+ state.instance_data_array[r_index].instance_uniforms_ofs = p_item->instance_allocated_shader_uniforms_offset;
+
+ state.instance_data_array[r_index].flags = base_flags | (state.instance_data_array[r_index == 0 ? 0 : r_index - 1].flags & (BATCH_FLAGS_DEFAULT_NORMAL_MAP_USED | BATCH_FLAGS_DEFAULT_SPECULAR_MAP_USED)); // Reset on each command for safety, keep canvastexture binding config.
Color blend_color = base_color;
GLES3::CanvasShaderData::BlendMode blend_mode = p_blend_mode;
@@ -2387,10 +2388,10 @@ void RasterizerCanvasGLES3::_prepare_canvas_texture(RID p_texture, RS::CanvasIte
state.canvas_instance_batches[state.current_batch_index].flags &= ~BATCH_FLAGS_DEFAULT_NORMAL_MAP_USED;
}
- state.instance_data_array[r_index].specular_shininess = uint32_t(CLAMP(ct->specular_color.a * 255.0, 0, 255)) << 24;
- state.instance_data_array[r_index].specular_shininess |= uint32_t(CLAMP(ct->specular_color.b * 255.0, 0, 255)) << 16;
- state.instance_data_array[r_index].specular_shininess |= uint32_t(CLAMP(ct->specular_color.g * 255.0, 0, 255)) << 8;
- state.instance_data_array[r_index].specular_shininess |= uint32_t(CLAMP(ct->specular_color.r * 255.0, 0, 255));
+ state.canvas_instance_batches[state.current_batch_index].specular_shininess = uint32_t(CLAMP(ct->specular_color.a * 255.0, 0, 255)) << 24;
+ state.canvas_instance_batches[state.current_batch_index].specular_shininess |= uint32_t(CLAMP(ct->specular_color.b * 255.0, 0, 255)) << 16;
+ state.canvas_instance_batches[state.current_batch_index].specular_shininess |= uint32_t(CLAMP(ct->specular_color.g * 255.0, 0, 255)) << 8;
+ state.canvas_instance_batches[state.current_batch_index].specular_shininess |= uint32_t(CLAMP(ct->specular_color.r * 255.0, 0, 255));
r_texpixel_size.x = 1.0 / float(size_cache.x);
r_texpixel_size.y = 1.0 / float(size_cache.y);
diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h
index aa6171580fb..3d6719a2966 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.h
+++ b/drivers/gles3/rasterizer_canvas_gles3.h
@@ -227,7 +227,7 @@ public:
};
};
uint32_t flags;
- uint32_t specular_shininess;
+ uint32_t instance_uniforms_ofs;
uint32_t lights[4];
};
@@ -279,6 +279,9 @@ public:
uint32_t primitive_points = 0;
uint32_t flags = 0;
+ uint32_t specular_shininess = 0.0;
+
+ bool lights_disabled = false;
};
// DataBuffer contains our per-frame data. I.e. the resources that are updated each frame.
diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl
index 5ed4dbcc85e..139e42a2511 100644
--- a/drivers/gles3/shaders/canvas.glsl
+++ b/drivers/gles3/shaders/canvas.glsl
@@ -83,7 +83,7 @@ layout(location = 15) in highp uvec4 attrib_H;
#endif
#define read_draw_data_flags attrib_G.z
-#define read_draw_data_specular_shininess attrib_G.w
+#define read_draw_data_instance_offset attrib_G.w
#define read_draw_data_lights attrib_H
// Varyings so the per-instance info can be used in the fragment shader
@@ -142,7 +142,7 @@ void main() {
#endif // !USE_ATTRIBUTES
#endif // USE_PRIMITIVE
- varying_F = uvec2(read_draw_data_flags, read_draw_data_specular_shininess);
+ varying_F = uvec2(read_draw_data_flags, read_draw_data_instance_offset);
varying_G = read_draw_data_lights;
vec4 instance_custom = vec4(0.0);
@@ -325,7 +325,7 @@ flat in vec4 varying_E;
flat in uvec2 varying_F;
flat in uvec4 varying_G;
#define read_draw_data_flags varying_F.x
-#define read_draw_data_specular_shininess varying_F.y
+#define read_draw_data_instance_offset varying_F.y
#define read_draw_data_lights varying_G
#ifndef DISABLE_LIGHTING
@@ -340,6 +340,7 @@ uniform sampler2D specular_texture; //texunit:-7
uniform sampler2D color_texture; //texunit:0
uniform mediump uint batch_flags;
+uniform highp uint specular_shininess_in;
layout(location = 0) out vec4 frag_color;
@@ -660,7 +661,7 @@ void main() {
if (specular_shininess_used || (using_light && normal_used && bool(batch_flags & BATCH_FLAGS_DEFAULT_SPECULAR_MAP_USED))) {
specular_shininess = texture(specular_texture, uv);
- specular_shininess *= godot_unpackUnorm4x8(read_draw_data_specular_shininess);
+ specular_shininess *= godot_unpackUnorm4x8(specular_shininess_in);
specular_shininess_used = true;
} else {
specular_shininess = vec4(1.0);
diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp
index 493f80a22b3..8df9fabeb40 100644
--- a/drivers/gles3/storage/material_storage.cpp
+++ b/drivers/gles3/storage/material_storage.cpp
@@ -1193,6 +1193,7 @@ MaterialStorage::MaterialStorage() {
actions.render_mode_defines["world_vertex_coords"] = "#define USE_WORLD_VERTEX_COORDS\n";
actions.global_buffer_array_variable = "global_shader_uniforms";
+ actions.instance_uniform_index_variable = "read_draw_data_instance_offset";
shaders.compiler_canvas.initialize(actions);
}
diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp
index f87dad1889b..864b2b5127f 100644
--- a/scene/main/canvas_item.cpp
+++ b/scene/main/canvas_item.cpp
@@ -552,6 +552,60 @@ int CanvasItem::get_light_mask() const {
return light_mask;
}
+const StringName *CanvasItem::_instance_shader_parameter_get_remap(const StringName &p_name) const {
+ StringName *r = instance_shader_parameter_property_remap.getptr(p_name);
+ if (!r) {
+ String s = p_name;
+ if (s.begins_with("instance_shader_parameters/")) {
+ StringName name = s.trim_prefix("instance_shader_parameters/");
+ instance_shader_parameter_property_remap[p_name] = name;
+ return instance_shader_parameter_property_remap.getptr(p_name);
+ }
+ return nullptr;
+ }
+ return r;
+}
+
+bool CanvasItem::_set(const StringName &p_name, const Variant &p_value) {
+ const StringName *r = _instance_shader_parameter_get_remap(p_name);
+ if (r) {
+ set_instance_shader_parameter(*r, p_value);
+ return true;
+ }
+ return false;
+}
+
+bool CanvasItem::_get(const StringName &p_name, Variant &r_ret) const {
+ const StringName *r = _instance_shader_parameter_get_remap(p_name);
+ if (r) {
+ r_ret = get_instance_shader_parameter(*r);
+ return true;
+ }
+
+ return false;
+}
+
+void CanvasItem::_get_property_list(List *p_list) const {
+ List pinfo;
+ RS::get_singleton()->canvas_item_get_instance_shader_parameter_list(get_canvas_item(), &pinfo);
+
+ for (PropertyInfo &pi : pinfo) {
+ bool has_def_value = false;
+ Variant def_value = RS::get_singleton()->canvas_item_get_instance_shader_parameter_default_value(get_canvas_item(), pi.name);
+ if (def_value.get_type() != Variant::NIL) {
+ has_def_value = true;
+ }
+ if (instance_shader_parameters.has(pi.name)) {
+ pi.usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE | (has_def_value ? (PROPERTY_USAGE_CHECKABLE | PROPERTY_USAGE_CHECKED) : PROPERTY_USAGE_NONE);
+ } else {
+ pi.usage = PROPERTY_USAGE_EDITOR | (has_def_value ? PROPERTY_USAGE_CHECKABLE : PROPERTY_USAGE_NONE); // Do not save if not changed.
+ }
+
+ pi.name = "instance_shader_parameters/" + pi.name;
+ p_list->push_back(pi);
+ }
+}
+
void CanvasItem::item_rect_changed(bool p_size_changed) {
ERR_MAIN_THREAD_GUARD;
if (p_size_changed) {
@@ -1082,6 +1136,26 @@ void CanvasItem::set_use_parent_material(bool p_use_parent_material) {
RS::get_singleton()->canvas_item_set_use_parent_material(canvas_item, p_use_parent_material);
}
+void CanvasItem::set_instance_shader_parameter(const StringName &p_name, const Variant &p_value) {
+ if (p_value.get_type() == Variant::NIL) {
+ Variant def_value = RS::get_singleton()->canvas_item_get_instance_shader_parameter_default_value(get_canvas_item(), p_name);
+ RS::get_singleton()->canvas_item_set_instance_shader_parameter(get_canvas_item(), p_name, def_value);
+ instance_shader_parameters.erase(p_value);
+ } else {
+ instance_shader_parameters[p_name] = p_value;
+ if (p_value.get_type() == Variant::OBJECT) {
+ RID tex_id = p_value;
+ RS::get_singleton()->canvas_item_set_instance_shader_parameter(get_canvas_item(), p_name, tex_id);
+ } else {
+ RS::get_singleton()->canvas_item_set_instance_shader_parameter(get_canvas_item(), p_name, p_value);
+ }
+ }
+}
+
+Variant CanvasItem::get_instance_shader_parameter(const StringName &p_name) const {
+ return RS::get_singleton()->canvas_item_get_instance_shader_parameter(get_canvas_item(), p_name);
+}
+
bool CanvasItem::get_use_parent_material() const {
ERR_READ_THREAD_GUARD_V(false);
return use_parent_material;
@@ -1244,6 +1318,9 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_material", "material"), &CanvasItem::set_material);
ClassDB::bind_method(D_METHOD("get_material"), &CanvasItem::get_material);
+ ClassDB::bind_method(D_METHOD("set_instance_shader_parameter", "name", "value"), &CanvasItem::set_instance_shader_parameter);
+ ClassDB::bind_method(D_METHOD("get_instance_shader_parameter", "name"), &CanvasItem::get_instance_shader_parameter);
+
ClassDB::bind_method(D_METHOD("set_use_parent_material", "enable"), &CanvasItem::set_use_parent_material);
ClassDB::bind_method(D_METHOD("get_use_parent_material"), &CanvasItem::get_use_parent_material);
diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h
index c74f8238e3c..9eb21b17d97 100644
--- a/scene/main/canvas_item.h
+++ b/scene/main/canvas_item.h
@@ -116,6 +116,8 @@ private:
TextureRepeat texture_repeat = TEXTURE_REPEAT_PARENT_NODE;
Ref material;
+ mutable HashMap instance_shader_parameters;
+ mutable HashMap instance_shader_parameter_property_remap;
mutable Transform2D global_transform;
mutable MTFlag global_invalid;
@@ -150,8 +152,13 @@ private:
void _update_texture_filter_changed(bool p_propagate);
void _notify_transform_deferred();
+ const StringName *_instance_shader_parameter_get_remap(const StringName &p_name) const;
protected:
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List *p_list) const;
+
virtual void _update_self_texture_repeat(RS::CanvasItemTextureRepeat p_texture_repeat);
virtual void _update_self_texture_filter(RS::CanvasItemTextureFilter p_texture_filter);
@@ -355,6 +362,9 @@ public:
virtual void set_material(const Ref &p_material);
Ref get_material() const;
+ void set_instance_shader_parameter(const StringName &p_name, const Variant &p_value);
+ Variant get_instance_shader_parameter(const StringName &p_name) const;
+
virtual void set_use_parent_material(bool p_use_parent_material);
bool get_use_parent_material() const;
diff --git a/servers/rendering/instance_uniforms.cpp b/servers/rendering/instance_uniforms.cpp
new file mode 100644
index 00000000000..6fa607fdb42
--- /dev/null
+++ b/servers/rendering/instance_uniforms.cpp
@@ -0,0 +1,186 @@
+/**************************************************************************/
+/* instance_uniforms.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "instance_uniforms.h"
+
+#include "rendering_server_globals.h"
+
+void InstanceUniforms::free(RID p_self) {
+ ERR_FAIL_COND(p_self.is_null());
+
+ if (is_allocated()) {
+ RSG::material_storage->global_shader_parameters_instance_free(p_self);
+ _location = -1;
+ }
+
+ _invalidate_items();
+}
+
+void InstanceUniforms::materials_start() {
+ _invalidate_items();
+}
+
+void InstanceUniforms::materials_append(RID p_material) {
+ ERR_FAIL_COND(p_material.is_null());
+
+ List params;
+ RSG::material_storage->material_get_instance_shader_parameters(p_material, ¶ms);
+
+ for (const RendererMaterialStorage::InstanceShaderParam &srcp : params) {
+ StringName name = srcp.info.name;
+ if (Item *ptr = _parameters.getptr(name); ptr) {
+ if (!ptr->is_valid()) {
+ _init_param(*ptr, srcp);
+ } else if (ptr->index != srcp.index) {
+ WARN_PRINT("More than one material in instance export the same instance shader uniform '" + srcp.info.name +
+ "', but they do it with different indices. Only the first one (in order) will display correctly.");
+ } else if (ptr->info.type != srcp.info.type) {
+ WARN_PRINT("More than one material in instance export the same instance shader uniform '" + srcp.info.name +
+ "', but they do it with different data types. Only the first one (in order) will display correctly.");
+ }
+ } else {
+ Item i;
+ _init_param(i, srcp);
+ _parameters[name] = i;
+ }
+ }
+}
+
+bool InstanceUniforms::materials_finish(RID p_self) {
+ ERR_FAIL_COND_V(p_self.is_null(), false);
+
+ if (_parameters.is_empty()) {
+ if (is_allocated()) {
+ free(p_self);
+ return true;
+ }
+ return false;
+ }
+
+ const bool should_alloc = !is_allocated();
+
+ if (should_alloc) {
+ _location = RSG::material_storage->global_shader_parameters_instance_allocate(p_self);
+ }
+
+ for (KeyValue &kv : _parameters) {
+ Item &i = kv.value;
+ if (i.is_valid()) {
+ RSG::material_storage->global_shader_parameters_instance_update(p_self, i.index, i.value, i.flags);
+ }
+ }
+
+ return should_alloc;
+}
+
+Variant InstanceUniforms::get(const StringName &p_name) const {
+ if (const Item *ptr = _parameters.getptr(p_name); ptr) {
+ return ptr->value;
+ }
+ return Variant();
+}
+
+void InstanceUniforms::set(RID p_self, const StringName &p_name, const Variant &p_value) {
+ ERR_FAIL_COND(p_self.is_null());
+ ERR_FAIL_COND(p_value.get_type() == Variant::OBJECT);
+
+ if (Item *ptr = _parameters.getptr(p_name); ptr) {
+ ptr->value = p_value;
+ if (ptr->is_valid()) {
+ RSG::material_storage->global_shader_parameters_instance_update(p_self, ptr->index, ptr->value, ptr->flags);
+ }
+ } else {
+ Item i; // Initialize in materials_finish.
+ i.value = p_value;
+ _parameters[p_name] = i;
+ }
+}
+
+Variant InstanceUniforms::get_default(const StringName &p_name) const {
+ if (const Item *ptr = _parameters.getptr(p_name); ptr) {
+ return ptr->default_value;
+ }
+ return Variant();
+}
+
+void InstanceUniforms::get_property_list(List &r_parameters) const {
+ Vector names;
+
+ // Invalid items won't be saved, but will remain in memory in case of shader compilation failure.
+ for (const KeyValue &kv : _parameters) {
+ if (kv.value.is_valid()) {
+ names.push_back(kv.key);
+ }
+ }
+
+ names.sort_custom();
+
+ for (const StringName &n : names) {
+ PropertyInfo pinfo = _parameters[n].info;
+ r_parameters.push_back(pinfo);
+ }
+}
+
+void InstanceUniforms::_init_param(Item &r_item, const RendererMaterialStorage::InstanceShaderParam &p_param) const {
+ r_item.index = p_param.index;
+ r_item.flags = 0;
+ r_item.info = p_param.info;
+ r_item.default_value = p_param.default_value;
+
+ if (r_item.default_value.get_type() == Variant::NIL) {
+ Callable::CallError cerr;
+ Variant::construct(r_item.info.type, r_item.default_value, nullptr, 0, cerr);
+ }
+
+ if (r_item.value.get_type() == Variant::NIL) {
+ r_item.value = r_item.default_value;
+ }
+
+ if (r_item.info.hint == PROPERTY_HINT_FLAGS) {
+ // HACK: Detect boolean flags count and prevent overhead.
+ switch (r_item.info.hint_string.length()) {
+ case 3: // "x,y"
+ r_item.flags = 1;
+ break;
+ case 5: // "x,y,z"
+ r_item.flags = 2;
+ break;
+ case 7: // "x,y,z,w"
+ r_item.flags = 3;
+ break;
+ }
+ }
+}
+
+void InstanceUniforms::_invalidate_items() {
+ for (KeyValue &kv : _parameters) {
+ kv.value.index = -1;
+ }
+}
diff --git a/servers/rendering/instance_uniforms.h b/servers/rendering/instance_uniforms.h
new file mode 100644
index 00000000000..768481681cf
--- /dev/null
+++ b/servers/rendering/instance_uniforms.h
@@ -0,0 +1,73 @@
+/**************************************************************************/
+/* instance_uniforms.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 INSTANCE_UNIFORMS_H
+#define INSTANCE_UNIFORMS_H
+
+#include "core/variant/variant.h"
+#include "servers/rendering/storage/material_storage.h"
+
+class InstanceUniforms {
+public:
+ void free(RID p_self);
+
+ void materials_start();
+ void materials_append(RID p_material);
+
+ // Assign location() to instance offset if materials_finish returns true.
+ bool materials_finish(RID p_self);
+
+ Variant get(const StringName &p_name) const;
+ void set(RID p_self, const StringName &p_name, const Variant &p_value);
+
+ Variant get_default(const StringName &p_name) const;
+ void get_property_list(List &r_parameters) const;
+
+ inline int32_t location() const { return _location; }
+ inline bool is_allocated() const { return _location != -1; }
+
+private:
+ struct Item {
+ int32_t index = -1;
+ int32_t flags = 0;
+ Variant value;
+ Variant default_value;
+ PropertyInfo info;
+
+ inline bool is_valid() const { return index != -1; }
+ };
+ int32_t _location = -1;
+ HashMap _parameters;
+
+ void _init_param(Item &r_item, const RendererMaterialStorage::InstanceShaderParam &p_param) const;
+ void _invalidate_items();
+};
+
+#endif // INSTANCE_UNIFORMS_H
diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp
index 22a9a4632d7..39f1cdef78e 100644
--- a/servers/rendering/renderer_canvas_cull.cpp
+++ b/servers/rendering/renderer_canvas_cull.cpp
@@ -44,6 +44,29 @@
// while not making lines appear too soft.
const static float FEATHER_SIZE = 1.25f;
+static RendererCanvasCull *_canvas_cull_singleton = nullptr;
+
+void RendererCanvasCull::_dependency_changed(Dependency::DependencyChangedNotification p_notification, DependencyTracker *p_tracker) {
+ Item *item = (Item *)p_tracker->userdata;
+
+ switch (p_notification) {
+ case Dependency::DEPENDENCY_CHANGED_MATERIAL: {
+ _canvas_cull_singleton->_item_queue_update(item, true);
+ } break;
+ default: {
+ } break;
+ }
+}
+
+void RendererCanvasCull::_dependency_deleted(const RID &p_dependency, DependencyTracker *p_tracker) {
+ Item *item = (Item *)p_tracker->userdata;
+
+ if (p_dependency == item->material) {
+ _canvas_cull_singleton->canvas_item_set_material(item->self, RID());
+ }
+ _canvas_cull_singleton->_item_queue_update(item, true);
+}
+
void RendererCanvasCull::_render_canvas_item_tree(RID p_to_render_target, Canvas::ChildItem *p_child_items, int p_child_item_count, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, RendererCanvasRender::Light *p_lights, RendererCanvasRender::Light *p_directional_lights, RenderingServer::CanvasItemTextureFilter p_default_filter, RenderingServer::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, uint32_t p_canvas_cull_mask, RenderingMethod::RenderInfo *r_render_info) {
RENDER_TIMESTAMP("Cull CanvasItem Tree");
@@ -525,6 +548,8 @@ RID RendererCanvasCull::canvas_item_allocate() {
}
void RendererCanvasCull::canvas_item_initialize(RID p_rid) {
canvas_item_owner.initialize_rid(p_rid);
+ Item *instance = canvas_item_owner.get_or_null(p_rid);
+ instance->self = p_rid;
}
void RendererCanvasCull::canvas_item_set_parent(RID p_item, RID p_parent) {
@@ -1844,6 +1869,7 @@ void RendererCanvasCull::canvas_item_clear(RID p_item) {
ERR_FAIL_NULL(canvas_item);
canvas_item->clear();
+
#ifdef DEBUG_ENABLED
if (debug_redraw) {
canvas_item->debug_redraw_time = debug_redraw_time;
@@ -1875,6 +1901,7 @@ void RendererCanvasCull::canvas_item_set_material(RID p_item, RID p_material) {
ERR_FAIL_NULL(canvas_item);
canvas_item->material = p_material;
+ _item_queue_update(canvas_item, true);
}
void RendererCanvasCull::canvas_item_set_use_parent_material(RID p_item, bool p_enable) {
@@ -1882,6 +1909,37 @@ void RendererCanvasCull::canvas_item_set_use_parent_material(RID p_item, bool p_
ERR_FAIL_NULL(canvas_item);
canvas_item->use_parent_material = p_enable;
+ _item_queue_update(canvas_item, true);
+}
+
+void RendererCanvasCull::canvas_item_set_instance_shader_parameter(RID p_item, const StringName &p_parameter, const Variant &p_value) {
+ Item *item = canvas_item_owner.get_or_null(p_item);
+ ERR_FAIL_NULL(item);
+
+ item->instance_uniforms.set(item->self, p_parameter, p_value);
+}
+
+Variant RendererCanvasCull::canvas_item_get_instance_shader_parameter(RID p_item, const StringName &p_parameter) const {
+ const Item *item = const_cast(this)->canvas_item_owner.get_or_null(p_item);
+ ERR_FAIL_NULL_V(item, Variant());
+
+ return item->instance_uniforms.get(p_parameter);
+}
+
+Variant RendererCanvasCull::canvas_item_get_instance_shader_parameter_default_value(RID p_item, const StringName &p_parameter) const {
+ const Item *item = const_cast(this)->canvas_item_owner.get_or_null(p_item);
+ ERR_FAIL_NULL_V(item, Variant());
+
+ return item->instance_uniforms.get_default(p_parameter);
+}
+
+void RendererCanvasCull::canvas_item_get_instance_shader_parameter_list(RID p_item, List *p_parameters) const {
+ ERR_FAIL_NULL(p_parameters);
+ const Item *item = const_cast(this)->canvas_item_owner.get_or_null(p_item);
+ ERR_FAIL_NULL(item);
+ const_cast(this)->update_dirty_items();
+
+ item->instance_uniforms.get_property_list(*p_parameters);
}
void RendererCanvasCull::canvas_item_set_visibility_notifier(RID p_item, bool p_enable, const Rect2 &p_area, const Callable &p_enter_callable, const Callable &p_exit_callable) {
@@ -2411,6 +2469,63 @@ Rect2 RendererCanvasCull::_debug_canvas_item_get_rect(RID p_item) {
return canvas_item->get_rect();
}
+void RendererCanvasCull::_item_queue_update(Item *p_item, bool p_update_dependencies) {
+ if (p_update_dependencies) {
+ p_item->update_dependencies = true;
+ }
+
+ if (!p_item->update_item.in_list()) {
+ _item_update_list.add(&p_item->update_item);
+ }
+}
+
+void RendererCanvasCull::update_dirty_items() {
+ while (_item_update_list.first()) {
+ _update_dirty_item(_item_update_list.first()->self());
+ }
+
+ // Instance updates may affect resources.
+ RSG::utilities->update_dirty_resources();
+}
+
+void RendererCanvasCull::_update_dirty_item(Item *p_item) {
+ if (p_item->update_dependencies) {
+ RID material = p_item->material;
+
+ if (p_item->use_parent_material) {
+ Item *parent = canvas_item_owner.get_or_null(p_item->parent);
+ while (parent != nullptr) {
+ material = parent->material;
+ if (!parent->use_parent_material) {
+ break;
+ }
+ parent = canvas_item_owner.get_or_null(parent->parent);
+ }
+ }
+
+ p_item->dependency_tracker.update_begin();
+
+ p_item->instance_uniforms.materials_start();
+
+ if (material.is_valid()) {
+ p_item->instance_uniforms.materials_append(material);
+ RSG::material_storage->material_update_dependency(material, &p_item->dependency_tracker);
+ }
+
+ if (p_item->instance_uniforms.materials_finish(p_item->self)) {
+ p_item->instance_allocated_shader_uniforms_offset = p_item->instance_uniforms.location();
+ }
+
+ p_item->dependency_tracker.update_end();
+ }
+ _item_update_list.remove(&p_item->update_item);
+ p_item->update_dependencies = false;
+}
+
+void RendererCanvasCull::update() {
+ update_dirty_items();
+}
+
bool RendererCanvasCull::free(RID p_rid) {
if (canvas_owner.owns(p_rid)) {
Canvas *canvas = canvas_owner.get_or_null(p_rid);
@@ -2468,11 +2583,9 @@ bool RendererCanvasCull::free(RID p_rid) {
visibility_notifier_allocator.free(canvas_item->visibility_notifier);
}
- /*
- if (canvas_item->material) {
- canvas_item->material->owners.erase(canvas_item);
- }
- */
+ canvas_item_set_material(canvas_item->self, RID());
+ canvas_item->instance_uniforms.free(canvas_item->self);
+ update_dirty_items();
if (canvas_item->canvas_group != nullptr) {
memdelete(canvas_item->canvas_group);
@@ -2634,6 +2747,8 @@ void RendererCanvasCull::InterpolationData::notify_free_canvas_light_occluder(RI
}
RendererCanvasCull::RendererCanvasCull() {
+ _canvas_cull_singleton = this;
+
z_list = (RendererCanvasRender::Item **)memalloc(z_range * sizeof(RendererCanvasRender::Item *));
z_last_list = (RendererCanvasRender::Item **)memalloc(z_range * sizeof(RendererCanvasRender::Item *));
@@ -2646,4 +2761,5 @@ RendererCanvasCull::RendererCanvasCull() {
RendererCanvasCull::~RendererCanvasCull() {
memfree(z_list);
memfree(z_last_list);
+ _canvas_cull_singleton = nullptr;
}
diff --git a/servers/rendering/renderer_canvas_cull.h b/servers/rendering/renderer_canvas_cull.h
index 9a088d94ed5..2c7e4128584 100644
--- a/servers/rendering/renderer_canvas_cull.h
+++ b/servers/rendering/renderer_canvas_cull.h
@@ -34,11 +34,16 @@
#include "core/templates/paged_allocator.h"
#include "renderer_compositor.h"
#include "renderer_viewport.h"
+#include "servers/rendering/instance_uniforms.h"
class RendererCanvasCull {
+ static void _dependency_changed(Dependency::DependencyChangedNotification p_notification, DependencyTracker *p_tracker);
+ static void _dependency_deleted(const RID &p_dependency, DependencyTracker *p_tracker);
+
public:
struct Item : public RendererCanvasRender::Item {
RID parent; // canvas it belongs to
+ RID self;
List- ::Element *E;
int z_index;
bool z_relative;
@@ -71,7 +76,14 @@ public:
VisibilityNotifierData *visibility_notifier = nullptr;
- Item() {
+ DependencyTracker dependency_tracker;
+ InstanceUniforms instance_uniforms;
+ SelfList
- update_item;
+
+ bool update_dependencies = false;
+
+ Item() :
+ update_item(this) {
children_order_dirty = true;
E = nullptr;
z_index = 0;
@@ -85,9 +97,16 @@ public:
ysort_xform = Transform2D();
ysort_index = 0;
ysort_parent_abs_z_index = 0;
+
+ dependency_tracker.userdata = this;
+ dependency_tracker.changed_callback = &RendererCanvasCull::_dependency_changed;
+ dependency_tracker.deleted_callback = &RendererCanvasCull::_dependency_deleted;
}
};
+ void _item_queue_update(Item *p_item, bool p_update_dependencies);
+ SelfList
- ::List _item_update_list;
+
struct ItemIndexSort {
_FORCE_INLINE_ bool operator()(const Item *p_left, const Item *p_right) const {
return p_left->index < p_right->index;
@@ -267,6 +286,11 @@ public:
void canvas_item_set_use_parent_material(RID p_item, bool p_enable);
+ void canvas_item_set_instance_shader_parameter(RID p_item, const StringName &p_parameter, const Variant &p_value);
+ void canvas_item_get_instance_shader_parameter_list(RID p_item, List *p_parameters) const;
+ Variant canvas_item_get_instance_shader_parameter(RID p_item, const StringName &p_parameter) const;
+ Variant canvas_item_get_instance_shader_parameter_default_value(RID p_item, const StringName &p_parameter) const;
+
void canvas_item_set_visibility_notifier(RID p_item, bool p_enable, const Rect2 &p_area, const Callable &p_enter_callable, const Callable &p_exit_callable);
void canvas_item_set_canvas_group_mode(RID p_item, RS::CanvasGroupMode p_mode, float p_clear_margin = 5.0, bool p_fit_empty = false, float p_fit_margin = 0.0, bool p_blur_mipmaps = false);
@@ -281,6 +305,8 @@ public:
RID canvas_light_allocate();
void canvas_light_initialize(RID p_rid);
+ void update();
+
void canvas_light_set_mode(RID p_light, RS::CanvasLightMode p_mode);
void canvas_light_attach_to_canvas(RID p_light, RID p_canvas);
void canvas_light_set_enabled(RID p_light, bool p_enabled);
@@ -344,6 +370,9 @@ public:
void canvas_item_set_default_texture_repeat(RID p_item, RS::CanvasItemTextureRepeat p_repeat);
void update_visibility_notifiers();
+ void update_dirty_items();
+
+ void _update_dirty_item(Item *p_item);
Rect2 _debug_canvas_item_get_rect(RID p_item);
diff --git a/servers/rendering/renderer_canvas_render.h b/servers/rendering/renderer_canvas_render.h
index 328fe32ea6c..64c354a8d3b 100644
--- a/servers/rendering/renderer_canvas_render.h
+++ b/servers/rendering/renderer_canvas_render.h
@@ -343,6 +343,8 @@ public:
RID material;
RID skeleton;
+ int32_t instance_allocated_shader_uniforms_offset = -1;
+
Item *next = nullptr;
struct CopyBackBuffer {
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
index 85b0a0763b8..4e8472ae0a7 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
@@ -1785,6 +1785,7 @@ RendererCanvasRenderRD::RendererCanvasRenderRD() {
actions.base_varying_index = 5;
actions.global_buffer_array_variable = "global_shader_uniforms.data";
+ actions.instance_uniform_index_variable = "draw_data.instance_uniforms_ofs";
shader.compiler.initialize(actions);
}
@@ -2239,7 +2240,7 @@ void RendererCanvasRenderRD::_render_batch_items(RenderTarget p_to_render_target
state.last_instance_index += instance_index;
}
-RendererCanvasRenderRD::InstanceData *RendererCanvasRenderRD::new_instance_data(float *p_world, uint32_t *p_lights, uint32_t p_base_flags, uint32_t p_index, TextureInfo *p_info) {
+RendererCanvasRenderRD::InstanceData *RendererCanvasRenderRD::new_instance_data(float *p_world, uint32_t *p_lights, uint32_t p_base_flags, uint32_t p_index, uint32_t p_uniforms_ofs, TextureInfo *p_info) {
InstanceData *instance_data = &state.instance_data_array[p_index];
// Zero out most fields.
for (int i = 0; i < 4; i++) {
@@ -2266,7 +2267,7 @@ RendererCanvasRenderRD::InstanceData *RendererCanvasRenderRD::new_instance_data(
instance_data->color_texture_pixel_size[0] = p_info->texpixel_size.width;
instance_data->color_texture_pixel_size[1] = p_info->texpixel_size.height;
- instance_data->pad1 = 0;
+ instance_data->instance_uniforms_ofs = p_uniforms_ofs;
return instance_data;
}
@@ -2284,6 +2285,7 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
Color base_color = p_item->final_modulate;
bool use_linear_colors = p_render_target.use_linear_colors;
uint32_t base_flags = 0;
+ uint32_t uniforms_ofs = static_cast(p_item->instance_allocated_shader_uniforms_offset);
bool reclip = false;
@@ -2383,7 +2385,7 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
r_current_batch->tex_info = tex_info;
}
- InstanceData *instance_data = new_instance_data(world, lights, base_flags, r_index, tex_info);
+ InstanceData *instance_data = new_instance_data(world, lights, base_flags, r_index, uniforms_ofs, tex_info);
Rect2 src_rect;
Rect2 dst_rect;
@@ -2484,7 +2486,7 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
r_current_batch->tex_info = tex_info;
}
- InstanceData *instance_data = new_instance_data(world, lights, base_flags, r_index, tex_info);
+ InstanceData *instance_data = new_instance_data(world, lights, base_flags, r_index, uniforms_ofs, tex_info);
Rect2 src_rect;
Rect2 dst_rect(np->rect.position.x, np->rect.position.y, np->rect.size.x, np->rect.size.y);
@@ -2566,7 +2568,7 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
r_current_batch->render_primitive = _primitive_type_to_render_primitive(polygon->primitive);
}
- InstanceData *instance_data = new_instance_data(world, lights, base_flags, r_index, tex_info);
+ InstanceData *instance_data = new_instance_data(world, lights, base_flags, r_index, uniforms_ofs, tex_info);
Color color = base_color;
if (use_linear_colors) {
@@ -2626,7 +2628,7 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
r_current_batch->tex_info = tex_info;
}
- InstanceData *instance_data = new_instance_data(world, lights, base_flags, r_index, tex_info);
+ InstanceData *instance_data = new_instance_data(world, lights, base_flags, r_index, uniforms_ofs, tex_info);
for (uint32_t j = 0; j < MIN(3u, primitive->point_count); j++) {
instance_data->points[j * 2 + 0] = primitive->points[j].x;
@@ -2644,7 +2646,7 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
_add_to_batch(r_index, r_batch_broken, r_current_batch);
if (primitive->point_count == 4) {
- instance_data = new_instance_data(world, lights, base_flags, r_index, tex_info);
+ instance_data = new_instance_data(world, lights, base_flags, r_index, uniforms_ofs, tex_info);
for (uint32_t j = 0; j < 3; j++) {
int offset = j == 0 ? 0 : 1;
@@ -2687,7 +2689,7 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
_prepare_batch_texture_info(m->texture, tex_state, tex_info);
}
r_current_batch->tex_info = tex_info;
- instance_data = new_instance_data(world, lights, base_flags, r_index, tex_info);
+ instance_data = new_instance_data(world, lights, base_flags, r_index, uniforms_ofs, tex_info);
r_current_batch->mesh_instance_count = 1;
_update_transform_2d_to_mat2x3(base_transform * draw_transform * m->transform, instance_data->world);
@@ -2714,7 +2716,7 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
_prepare_batch_texture_info(mm->texture, tex_state, tex_info);
}
r_current_batch->tex_info = tex_info;
- instance_data = new_instance_data(world, lights, base_flags, r_index, tex_info);
+ instance_data = new_instance_data(world, lights, base_flags, r_index, uniforms_ofs, tex_info);
r_current_batch->flags |= 1; // multimesh, trails disabled
@@ -2736,7 +2738,7 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
_prepare_batch_texture_info(pt->texture, tex_state, tex_info);
}
r_current_batch->tex_info = tex_info;
- instance_data = new_instance_data(world, lights, base_flags, r_index, tex_info);
+ instance_data = new_instance_data(world, lights, base_flags, r_index, uniforms_ofs, tex_info);
uint32_t divisor = 1;
r_current_batch->mesh_instance_count = particles_storage->particles_get_amount(pt->particles, divisor);
@@ -2859,7 +2861,7 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
r_current_batch->tex_info = tex_info;
}
- InstanceData *instance_data = new_instance_data(world, lights, base_flags, r_index, tex_info);
+ InstanceData *instance_data = new_instance_data(world, lights, base_flags, r_index, uniforms_ofs, tex_info);
Rect2 src_rect;
Rect2 dst_rect;
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
index 6d8275db2e3..7d04aa4e75d 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
@@ -342,7 +342,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
struct InstanceData {
float world[6];
uint32_t flags;
- uint32_t pad1;
+ uint32_t instance_uniforms_ofs;
union {
//rect
struct {
@@ -605,7 +605,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
void _record_item_commands(const Item *p_item, RenderTarget p_render_target, const Transform2D &p_base_transform, Item *&r_current_clip, Light *p_lights, uint32_t &r_index, bool &r_batch_broken, bool &r_sdf_used, Batch *&r_current_batch);
void _render_batch(RD::DrawListID p_draw_list, CanvasShaderData *p_shader_data, RenderingDevice::FramebufferFormatID p_framebuffer_format, Light *p_lights, Batch const *p_batch, RenderingMethod::RenderInfo *r_render_info = nullptr);
void _prepare_batch_texture_info(RID p_texture, TextureState &p_state, TextureInfo *p_info);
- InstanceData *new_instance_data(float *p_world, uint32_t *p_lights, uint32_t p_base_flags, uint32_t p_index, TextureInfo *p_info);
+ InstanceData *new_instance_data(float *p_world, uint32_t *p_lights, uint32_t p_base_flags, uint32_t p_index, uint32_t p_uniforms_ofs, TextureInfo *p_info);
[[nodiscard]] Batch *_new_batch(bool &r_batch_broken);
void _add_to_batch(uint32_t &r_index, bool &r_batch_broken, Batch *&r_current_batch);
void _allocate_instance_buffer();
diff --git a/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl b/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl
index f5a806fbca3..ba228f04328 100644
--- a/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl
@@ -23,7 +23,7 @@ struct InstanceData {
vec2 world_y;
vec2 world_ofs;
uint flags;
- uint pad2;
+ uint instance_uniforms_ofs;
#ifdef USE_PRIMITIVE
vec2 points[3];
vec2 uvs[3];
diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp
index 709c0e76c19..1446497c8d2 100644
--- a/servers/rendering/renderer_scene_cull.cpp
+++ b/servers/rendering/renderer_scene_cull.cpp
@@ -701,7 +701,7 @@ void RendererSceneCull::instance_set_base(RID p_instance, RID p_base) {
geom->geometry_instance->set_use_baked_light(instance->baked_light);
geom->geometry_instance->set_use_dynamic_gi(instance->dynamic_gi);
geom->geometry_instance->set_use_lightmap(RID(), instance->lightmap_uv_scale, instance->lightmap_slice_index);
- geom->geometry_instance->set_instance_shader_uniforms_offset(instance->instance_allocated_shader_uniforms_offset);
+ geom->geometry_instance->set_instance_shader_uniforms_offset(instance->instance_uniforms.location());
geom->geometry_instance->set_cast_double_sided_shadows(instance->cast_shadows == RS::SHADOW_CASTING_SETTING_DOUBLE_SIDED);
if (instance->lightmap_sh.size() == 9) {
geom->geometry_instance->set_lightmap_capture(instance->lightmap_sh.ptr());
@@ -1636,58 +1636,21 @@ void RendererSceneCull::instance_geometry_set_shader_parameter(RID p_instance, c
Instance *instance = instance_owner.get_or_null(p_instance);
ERR_FAIL_NULL(instance);
- ERR_FAIL_COND(p_value.get_type() == Variant::OBJECT);
-
- HashMap::Iterator E = instance->instance_shader_uniforms.find(p_parameter);
-
- if (!E) {
- Instance::InstanceShaderParameter isp;
- isp.index = -1;
- isp.info = PropertyInfo();
- isp.value = p_value;
- instance->instance_shader_uniforms[p_parameter] = isp;
- } else {
- E->value.value = p_value;
- if (E->value.index >= 0 && instance->instance_allocated_shader_uniforms) {
- int flags_count = 0;
- if (E->value.info.hint == PROPERTY_HINT_FLAGS) {
- // A small hack to detect boolean flags count and prevent overhead.
- switch (E->value.info.hint_string.length()) {
- case 3: // "x,y"
- flags_count = 1;
- break;
- case 5: // "x,y,z"
- flags_count = 2;
- break;
- case 7: // "x,y,z,w"
- flags_count = 3;
- break;
- }
- }
- //update directly
- RSG::material_storage->global_shader_parameters_instance_update(p_instance, E->value.index, p_value, flags_count);
- }
- }
+ instance->instance_uniforms.set(instance->self, p_parameter, p_value);
}
Variant RendererSceneCull::instance_geometry_get_shader_parameter(RID p_instance, const StringName &p_parameter) const {
const Instance *instance = instance_owner.get_or_null(p_instance);
ERR_FAIL_NULL_V(instance, Variant());
- if (instance->instance_shader_uniforms.has(p_parameter)) {
- return instance->instance_shader_uniforms[p_parameter].value;
- }
- return Variant();
+ return instance->instance_uniforms.get(p_parameter);
}
Variant RendererSceneCull::instance_geometry_get_shader_parameter_default_value(RID p_instance, const StringName &p_parameter) const {
const Instance *instance = instance_owner.get_or_null(p_instance);
ERR_FAIL_NULL_V(instance, Variant());
- if (instance->instance_shader_uniforms.has(p_parameter)) {
- return instance->instance_shader_uniforms[p_parameter].default_value;
- }
- return Variant();
+ return instance->instance_uniforms.get_default(p_parameter);
}
void RendererSceneCull::mesh_generate_pipelines(RID p_mesh, bool p_background_compilation) {
@@ -1699,20 +1662,13 @@ uint32_t RendererSceneCull::get_pipeline_compilations(RS::PipelineSource p_sourc
}
void RendererSceneCull::instance_geometry_get_shader_parameter_list(RID p_instance, List *p_parameters) const {
+ ERR_FAIL_NULL(p_parameters);
const Instance *instance = instance_owner.get_or_null(p_instance);
ERR_FAIL_NULL(instance);
update_dirty_instances();
- Vector names;
- for (const KeyValue &E : instance->instance_shader_uniforms) {
- names.push_back(E.key);
- }
- names.sort_custom();
- for (int i = 0; i < names.size(); i++) {
- PropertyInfo pinfo = instance->instance_shader_uniforms[names[i]].info;
- p_parameters->push_back(pinfo);
- }
+ instance->instance_uniforms.get_property_list(*p_parameters);
}
void RendererSceneCull::_update_instance(Instance *p_instance) const {
@@ -4047,34 +4003,6 @@ void RendererSceneCull::render_particle_colliders() {
}
}
-void RendererSceneCull::_update_instance_shader_uniforms_from_material(HashMap &isparams, const HashMap &existing_isparams, RID p_material) const {
- List plist;
- RSG::material_storage->material_get_instance_shader_parameters(p_material, &plist);
- for (const RendererMaterialStorage::InstanceShaderParam &E : plist) {
- StringName name = E.info.name;
- if (isparams.has(name)) {
- if (isparams[name].info.type != E.info.type) {
- WARN_PRINT("More than one material in instance export the same instance shader uniform '" + E.info.name + "', but they do it with different data types. Only the first one (in order) will display correctly.");
- }
- if (isparams[name].index != E.index) {
- WARN_PRINT("More than one material in instance export the same instance shader uniform '" + E.info.name + "', but they do it with different indices. Only the first one (in order) will display correctly.");
- }
- continue; //first one found always has priority
- }
-
- Instance::InstanceShaderParameter isp;
- isp.index = E.index;
- isp.info = E.info;
- isp.default_value = E.default_value;
- if (existing_isparams.has(name)) {
- isp.value = existing_isparams[name].value;
- } else {
- isp.value = E.default_value;
- }
- isparams[name] = isp;
- }
-}
-
void RendererSceneCull::_update_dirty_instance(Instance *p_instance) const {
if (p_instance->update_aabb) {
_update_instance_aabb(p_instance);
@@ -4118,7 +4046,8 @@ void RendererSceneCull::_update_dirty_instance(Instance *p_instance) const {
bool can_cast_shadows = true;
bool is_animated = false;
- HashMap isparams;
+
+ p_instance->instance_uniforms.materials_start();
if (p_instance->cast_shadows == RS::SHADOW_CASTING_SETTING_OFF) {
can_cast_shadows = false;
@@ -4129,7 +4058,7 @@ void RendererSceneCull::_update_dirty_instance(Instance *p_instance) const {
can_cast_shadows = false;
}
is_animated = RSG::material_storage->material_is_animated(p_instance->material_override);
- _update_instance_shader_uniforms_from_material(isparams, p_instance->instance_shader_uniforms, p_instance->material_override);
+ p_instance->instance_uniforms.materials_append(p_instance->material_override);
} else {
if (p_instance->base_type == RS::INSTANCE_MESH) {
RID mesh = p_instance->base;
@@ -4151,7 +4080,7 @@ void RendererSceneCull::_update_dirty_instance(Instance *p_instance) const {
is_animated = true;
}
- _update_instance_shader_uniforms_from_material(isparams, p_instance->instance_shader_uniforms, mat);
+ p_instance->instance_uniforms.materials_append(mat);
RSG::material_storage->material_update_dependency(mat, &p_instance->dependency_tracker);
}
@@ -4182,7 +4111,7 @@ void RendererSceneCull::_update_dirty_instance(Instance *p_instance) const {
is_animated = true;
}
- _update_instance_shader_uniforms_from_material(isparams, p_instance->instance_shader_uniforms, mat);
+ p_instance->instance_uniforms.materials_append(mat);
RSG::material_storage->material_update_dependency(mat, &p_instance->dependency_tracker);
}
@@ -4220,7 +4149,7 @@ void RendererSceneCull::_update_dirty_instance(Instance *p_instance) const {
is_animated = true;
}
- _update_instance_shader_uniforms_from_material(isparams, p_instance->instance_shader_uniforms, mat);
+ p_instance->instance_uniforms.materials_append(mat);
RSG::material_storage->material_update_dependency(mat, &p_instance->dependency_tracker);
}
@@ -4236,7 +4165,7 @@ void RendererSceneCull::_update_dirty_instance(Instance *p_instance) const {
if (p_instance->material_overlay.is_valid()) {
can_cast_shadows = can_cast_shadows && RSG::material_storage->material_casts_shadows(p_instance->material_overlay);
is_animated = is_animated || RSG::material_storage->material_is_animated(p_instance->material_overlay);
- _update_instance_shader_uniforms_from_material(isparams, p_instance->instance_shader_uniforms, p_instance->material_overlay);
+ p_instance->instance_uniforms.materials_append(p_instance->material_overlay);
}
if (can_cast_shadows != geom->can_cast_shadows) {
@@ -4250,41 +4179,9 @@ void RendererSceneCull::_update_dirty_instance(Instance *p_instance) const {
}
geom->material_is_animated = is_animated;
- p_instance->instance_shader_uniforms = isparams;
- if (p_instance->instance_allocated_shader_uniforms != (p_instance->instance_shader_uniforms.size() > 0)) {
- p_instance->instance_allocated_shader_uniforms = (p_instance->instance_shader_uniforms.size() > 0);
- if (p_instance->instance_allocated_shader_uniforms) {
- p_instance->instance_allocated_shader_uniforms_offset = RSG::material_storage->global_shader_parameters_instance_allocate(p_instance->self);
- ERR_FAIL_NULL(geom->geometry_instance);
- geom->geometry_instance->set_instance_shader_uniforms_offset(p_instance->instance_allocated_shader_uniforms_offset);
-
- for (const KeyValue &E : p_instance->instance_shader_uniforms) {
- if (E.value.value.get_type() != Variant::NIL) {
- int flags_count = 0;
- if (E.value.info.hint == PROPERTY_HINT_FLAGS) {
- // A small hack to detect boolean flags count and prevent overhead.
- switch (E.value.info.hint_string.length()) {
- case 3: // "x,y"
- flags_count = 1;
- break;
- case 5: // "x,y,z"
- flags_count = 2;
- break;
- case 7: // "x,y,z,w"
- flags_count = 3;
- break;
- }
- }
- RSG::material_storage->global_shader_parameters_instance_update(p_instance->self, E.value.index, E.value.value, flags_count);
- }
- }
- } else {
- RSG::material_storage->global_shader_parameters_instance_free(p_instance->self);
- p_instance->instance_allocated_shader_uniforms_offset = -1;
- ERR_FAIL_NULL(geom->geometry_instance);
- geom->geometry_instance->set_instance_shader_uniforms_offset(-1);
- }
+ if (p_instance->instance_uniforms.materials_finish(p_instance->self)) {
+ geom->geometry_instance->set_instance_shader_uniforms_offset(p_instance->instance_uniforms.location());
}
}
@@ -4379,10 +4276,7 @@ bool RendererSceneCull::free(RID p_rid) {
instance_geometry_set_material_overlay(p_rid, RID());
instance_attach_skeleton(p_rid, RID());
- if (instance->instance_allocated_shader_uniforms) {
- //free the used shader parameters
- RSG::material_storage->global_shader_parameters_instance_free(instance->self);
- }
+ instance->instance_uniforms.free(instance->self);
update_dirty_instances(); //in case something changed this
instance_owner.free(p_rid);
diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h
index d56daea240d..276f76324f1 100644
--- a/servers/rendering/renderer_scene_cull.h
+++ b/servers/rendering/renderer_scene_cull.h
@@ -40,6 +40,7 @@
#include "core/templates/pass_func.h"
#include "core/templates/rid_owner.h"
#include "core/templates/self_list.h"
+#include "servers/rendering/instance_uniforms.h"
#include "servers/rendering/renderer_scene_occlusion_cull.h"
#include "servers/rendering/renderer_scene_render.h"
#include "servers/rendering/rendering_method.h"
@@ -460,16 +461,7 @@ public:
AABB transformed_aabb;
AABB prev_transformed_aabb;
- struct InstanceShaderParameter {
- int32_t index = -1;
- Variant value;
- Variant default_value;
- PropertyInfo info;
- };
-
- HashMap instance_shader_uniforms;
- bool instance_allocated_shader_uniforms = false;
- int32_t instance_allocated_shader_uniforms_offset = -1;
+ InstanceUniforms instance_uniforms;
//
@@ -1095,8 +1087,6 @@ public:
virtual void instance_geometry_set_lightmap(RID p_instance, RID p_lightmap, const Rect2 &p_lightmap_uv_scale, int p_slice_index);
virtual void instance_geometry_set_lod_bias(RID p_instance, float p_lod_bias);
- void _update_instance_shader_uniforms_from_material(HashMap &isparams, const HashMap &existing_isparams, RID p_material) const;
-
virtual void instance_geometry_set_shader_parameter(RID p_instance, const StringName &p_parameter, const Variant &p_value);
virtual void instance_geometry_get_shader_parameter_list(RID p_instance, List *p_parameters) const;
virtual Variant instance_geometry_get_shader_parameter(RID p_instance, const StringName &p_parameter) const;
diff --git a/servers/rendering/rendering_server_default.cpp b/servers/rendering/rendering_server_default.cpp
index b5d0d014791..eb7721adcea 100644
--- a/servers/rendering/rendering_server_default.cpp
+++ b/servers/rendering/rendering_server_default.cpp
@@ -77,6 +77,7 @@ void RenderingServerDefault::_draw(bool p_swap_buffers, double frame_step) {
RENDER_TIMESTAMP("Prepare Render Frame");
RSG::scene->update(); //update scenes stuff before updating instances
+ RSG::canvas->update();
frame_setup_time = double(OS::get_singleton()->get_ticks_usec() - time_usec) / 1000.0;
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index 5cc96a01ccf..00b7f362496 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -990,6 +990,11 @@ public:
FUNC2(canvas_item_set_material, RID, RID)
+ FUNC3(canvas_item_set_instance_shader_parameter, RID, const StringName &, const Variant &)
+ FUNC2RC(Variant, canvas_item_get_instance_shader_parameter, RID, const StringName &)
+ FUNC2RC(Variant, canvas_item_get_instance_shader_parameter_default_value, RID, const StringName &)
+ FUNC2C(canvas_item_get_instance_shader_parameter_list, RID, List *)
+
FUNC2(canvas_item_set_use_parent_material, RID, bool)
FUNC5(canvas_item_set_visibility_notifier, RID, bool, const Rect2 &, const Callable &, const Callable &)
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index ac078390cd9..14a5ee6cd1e 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -9143,7 +9143,7 @@ Error ShaderLanguage::_parse_shader(const HashMap &p_f
}
}
#endif // DEBUG_ENABLED
- if (shader_type_identifier != StringName() && String(shader_type_identifier) != "spatial") {
+ if (shader_type_identifier != StringName() && String(shader_type_identifier) != "spatial" && String(shader_type_identifier) != "canvas_item") {
_set_error(vformat(RTR("Uniform instances are not yet implemented for '%s' shaders."), shader_type_identifier));
return ERR_PARSE_ERROR;
}
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index 9d3acdd42bc..5cd07ee8950 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -2052,6 +2052,12 @@ TypedArray RenderingServer::_instance_geometry_get_shader_parameter_
return convert_property_list(¶ms);
}
+TypedArray RenderingServer::_canvas_item_get_instance_shader_parameter_list(RID p_instance) const {
+ List params;
+ canvas_item_get_instance_shader_parameter_list(p_instance, ¶ms);
+ return convert_property_list(¶ms);
+}
+
TypedArray RenderingServer::_bake_render_uv2(RID p_base, const TypedArray &p_material_overrides, const Size2i &p_image_size) {
TypedArray mat_overrides;
for (int i = 0; i < p_material_overrides.size(); i++) {
@@ -3291,6 +3297,11 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("canvas_item_set_material", "item", "material"), &RenderingServer::canvas_item_set_material);
ClassDB::bind_method(D_METHOD("canvas_item_set_use_parent_material", "item", "enabled"), &RenderingServer::canvas_item_set_use_parent_material);
+ ClassDB::bind_method(D_METHOD("canvas_item_set_instance_shader_parameter", "instance", "parameter", "value"), &RenderingServer::canvas_item_set_instance_shader_parameter);
+ ClassDB::bind_method(D_METHOD("canvas_item_get_instance_shader_parameter", "instance", "parameter"), &RenderingServer::canvas_item_get_instance_shader_parameter);
+ ClassDB::bind_method(D_METHOD("canvas_item_get_instance_shader_parameter_default_value", "instance", "parameter"), &RenderingServer::canvas_item_get_instance_shader_parameter_default_value);
+ ClassDB::bind_method(D_METHOD("canvas_item_get_instance_shader_parameter_list", "instance"), &RenderingServer::_canvas_item_get_instance_shader_parameter_list);
+
ClassDB::bind_method(D_METHOD("canvas_item_set_visibility_notifier", "item", "enable", "area", "enter_callable", "exit_callable"), &RenderingServer::canvas_item_set_visibility_notifier);
ClassDB::bind_method(D_METHOD("canvas_item_set_canvas_group_mode", "item", "mode", "clear_margin", "fit_empty", "fit_margin", "blur_mipmaps"), &RenderingServer::canvas_item_set_canvas_group_mode, DEFVAL(5.0), DEFVAL(false), DEFVAL(0.0), DEFVAL(false));
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index 23dbc821135..0c19cd0bf86 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -1546,6 +1546,11 @@ public:
virtual void canvas_item_set_use_parent_material(RID p_item, bool p_enable) = 0;
+ virtual void canvas_item_set_instance_shader_parameter(RID p_item, const StringName &, const Variant &p_value) = 0;
+ virtual Variant canvas_item_get_instance_shader_parameter(RID p_item, const StringName &) const = 0;
+ virtual Variant canvas_item_get_instance_shader_parameter_default_value(RID p_item, const StringName &) const = 0;
+ virtual void canvas_item_get_instance_shader_parameter_list(RID p_item, List *p_parameters) const = 0;
+
virtual void canvas_item_set_visibility_notifier(RID p_item, bool p_enable, const Rect2 &p_area, const Callable &p_enter_callbable, const Callable &p_exit_callable) = 0;
enum CanvasGroupMode {
@@ -1832,6 +1837,7 @@ private:
void _mesh_add_surface(RID p_mesh, const Dictionary &p_surface);
Dictionary _mesh_get_surface(RID p_mesh, int p_idx);
TypedArray _instance_geometry_get_shader_parameter_list(RID p_instance) const;
+ TypedArray _canvas_item_get_instance_shader_parameter_list(RID p_item) const;
TypedArray _bake_render_uv2(RID p_base, const TypedArray &p_material_overrides, const Size2i &p_image_size);
void _particles_set_trail_bind_poses(RID p_particles, const TypedArray &p_bind_poses);
#ifdef TOOLS_ENABLED