diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml
index 0a0223c550c8..1eb8a362ba19 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.
+
+
@@ -557,6 +564,17 @@
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).
+ [b]Note:[/b] Per-instance shader uniforms are currently only available in 3D, so there is no 2D equivalent of this method.
+
+
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index 58e88a3bdc1f..d66b7ed42468 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].
+ [b]Note:[/b] Per-instance shader parameter names are case-sensitive.
+
+
+
+
+
+
+
+ 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]. Equivalent to [method CanvasItem.get_instance_shader_parameter].
+
+
@@ -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/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp
index 940b0d8dd13f..d3460d079e2f 100644
--- a/scene/main/canvas_item.cpp
+++ b/scene/main/canvas_item.cpp
@@ -585,10 +585,10 @@ bool CanvasItem::_get(const StringName &p_name, Variant &r_ret) const {
void CanvasItem::_get_property_list(List *p_list) const {
List pinfo;
- RS::get_singleton()->instance_item_get_shader_parameter_list(get_canvas_item(), &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()->instance_item_get_shader_parameter_default_value(get_canvas_item(), pi.name);
+ 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;
}
@@ -1135,22 +1135,22 @@ void CanvasItem::set_use_parent_material(bool 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()->instance_item_get_shader_parameter_default_value(get_canvas_item(), p_name);
- RS::get_singleton()->instance_item_set_shader_parameter(get_canvas_item(), p_name, def_value);
+ 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()->instance_item_set_shader_parameter(get_canvas_item(), p_name, tex_id);
+ RS::get_singleton()->canvas_item_set_instance_shader_parameter(get_canvas_item(), p_name, tex_id);
} else {
- RS::get_singleton()->instance_item_set_shader_parameter(get_canvas_item(), p_name, p_value);
+ 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()->instance_item_get_shader_parameter(get_canvas_item(), p_name);
+ return RS::get_singleton()->canvas_item_get_instance_shader_parameter(get_canvas_item(), p_name);
}
bool CanvasItem::get_use_parent_material() const {
@@ -1315,6 +1315,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/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp
index 8d0af04d7e73..539a129dbc4d 100644
--- a/servers/rendering/renderer_canvas_cull.cpp
+++ b/servers/rendering/renderer_canvas_cull.cpp
@@ -1887,7 +1887,7 @@ void RendererCanvasCull::_update_instance_shader_uniforms_from_material(HashMap<
}
}
-void RendererCanvasCull::instance_item_set_shader_parameter(RID p_item, const StringName &p_parameter, const Variant &p_value) {
+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_COND(!item);
@@ -1910,8 +1910,8 @@ void RendererCanvasCull::instance_item_set_shader_parameter(RID p_item, const St
}
}
-Variant RendererCanvasCull::instance_item_get_shader_parameter(RID p_instance, const StringName &p_parameter) const {
- const Item *item = const_cast(this)->canvas_item_owner.get_or_null(p_instance);
+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_COND_V(!item, Variant());
if (item->instance_shader_uniforms.has(p_parameter)) {
@@ -1920,8 +1920,8 @@ Variant RendererCanvasCull::instance_item_get_shader_parameter(RID p_instance, c
return Variant();
}
-Variant RendererCanvasCull::instance_item_get_shader_parameter_default_value(RID p_instance, const StringName &p_parameter) const {
- const Item *item = const_cast(this)->canvas_item_owner.get_or_null(p_instance);
+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_COND_V(!item, Variant());
if (item->instance_shader_uniforms.has(p_parameter)) {
@@ -1930,8 +1930,8 @@ Variant RendererCanvasCull::instance_item_get_shader_parameter_default_value(RID
return Variant();
}
-void RendererCanvasCull::instance_item_get_shader_parameter_list(RID p_instance, List *p_parameters) const {
- const Item *item = const_cast(this)->canvas_item_owner.get_or_null(p_instance);
+void RendererCanvasCull::canvas_item_get_instance_shader_parameter_list(RID p_item, List *p_parameters) const {
+ const Item *item = const_cast(this)->canvas_item_owner.get_or_null(p_item);
ERR_FAIL_COND(!item);
const_cast(this)->update_dirty_instances();
@@ -2491,7 +2491,22 @@ void RendererCanvasCull::update_dirty_instances() {
for (const KeyValue &E : item->instance_shader_uniforms) {
if (E.value.value.get_type() != Variant::NIL) {
- RSG::material_storage->global_shader_parameters_instance_update(item->self, E.value.index, E.value.value);
+ 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(item->self, E.value.index, E.value.value, flags_count);
}
}
} else {
@@ -2565,11 +2580,10 @@ 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);
+ if (canvas_item->instance_allocated_shader_uniforms) {
+ RSG::material_storage->global_shader_parameters_instance_free(canvas_item->self);
}
- */
+ update_dirty_instances();
if (canvas_item->canvas_group != nullptr) {
memdelete(canvas_item->canvas_group);
diff --git a/servers/rendering/renderer_canvas_cull.h b/servers/rendering/renderer_canvas_cull.h
index 80c48be1bf7d..796850a11e3d 100644
--- a/servers/rendering/renderer_canvas_cull.h
+++ b/servers/rendering/renderer_canvas_cull.h
@@ -82,7 +82,6 @@ class RendererCanvasCull {
HashMap instance_shader_uniforms;
bool instance_allocated_shader_uniforms = false;
- int32_t instance_allocated_shader_uniforms_offset = -1;
SelfList- update_item;
Item() :
@@ -280,10 +279,10 @@ class RendererCanvasCull {
void canvas_item_set_use_parent_material(RID p_item, bool p_enable);
void _update_instance_shader_uniforms_from_material(HashMap &isparams, const HashMap &existing_isparams, RID p_material);
- void instance_item_set_shader_parameter(RID p_item, const StringName &p_parameter, const Variant &p_value);
- void instance_item_get_shader_parameter_list(RID p_instance, List *p_parameters) const;
- Variant instance_item_get_shader_parameter(RID p_instance, const StringName &p_parameter) const;
- Variant instance_item_get_shader_parameter_default_value(RID p_instance, const StringName &p_parameter) const;
+ 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);
diff --git a/servers/rendering/renderer_canvas_render.h b/servers/rendering/renderer_canvas_render.h
index c57abee1659f..339771dfb6b2 100644
--- a/servers/rendering/renderer_canvas_render.h
+++ b/servers/rendering/renderer_canvas_render.h
@@ -343,6 +343,8 @@ class RendererCanvasRender {
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 88a22c6fc348..6fe8689b33df 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
@@ -1828,6 +1828,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);
}
@@ -2343,6 +2344,8 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
instance_data->color_texture_pixel_size[1] = r_current_batch->tex_texpixel_size.height;
instance_data->specular_shininess = r_current_batch->tex_specular_shininess;
+ instance_data->instance_uniforms_ofs = uint32_t(p_item->instance_allocated_shader_uniforms_offset);
+
return instance_data;
};
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
index 8d90cd23ce25..db316cdba725 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
@@ -361,6 +361,8 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
};
float color_texture_pixel_size[2];
uint32_t lights[4];
+ uint32_t instance_uniforms_ofs;
+ uint32_t pad2[3];
};
struct PushConstant {
diff --git a/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl b/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl
index 7cf5b4576e6a..5bee1839ed38 100644
--- a/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl
@@ -52,6 +52,8 @@ struct InstanceData {
#endif
vec2 color_texture_pixel_size;
uint lights[4];
+ uint instance_uniforms_ofs;
+ uint pad2[3];
};
layout(set = 4, binding = 0, std430) restrict readonly buffer DrawData {
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index 496585fa99b7..da63f34540de 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -923,10 +923,10 @@ class RenderingServerDefault : public RenderingServer {
FUNC2(canvas_item_set_material, RID, RID)
- FUNC3(instance_item_set_shader_parameter, RID, const StringName &, const Variant &)
- FUNC2RC(Variant, instance_item_get_shader_parameter, RID, const StringName &)
- FUNC2RC(Variant, instance_item_get_shader_parameter_default_value, RID, const StringName &)
- FUNC2C(instance_item_get_shader_parameter_list, RID, List *)
+ 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)
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index 287965cafb57..21d3affa2e6b 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -2050,9 +2050,9 @@ TypedArray RenderingServer::_instance_geometry_get_shader_parameter_
return convert_property_list(¶ms);
}
-TypedArray RenderingServer::_instance_item_get_shader_parameter_list(RID p_instance) const {
+TypedArray RenderingServer::_canvas_item_get_instance_shader_parameter_list(RID p_instance) const {
List params;
- instance_item_get_shader_parameter_list(p_instance, ¶ms);
+ canvas_item_get_instance_shader_parameter_list(p_instance, ¶ms);
return convert_property_list(¶ms);
}
@@ -3276,10 +3276,10 @@ 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("instance_item_set_shader_parameter", "instance", "parameter", "value"), &RenderingServer::instance_item_set_shader_parameter);
- ClassDB::bind_method(D_METHOD("instance_item_get_shader_parameter", "instance", "parameter"), &RenderingServer::instance_item_get_shader_parameter);
- ClassDB::bind_method(D_METHOD("instance_item_get_shader_parameter_default_value", "instance", "parameter"), &RenderingServer::instance_item_get_shader_parameter_default_value);
- ClassDB::bind_method(D_METHOD("instance_item_get_shader_parameter_list", "instance"), &RenderingServer::_instance_item_get_shader_parameter_list);
+ 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 b08d4601c0ef..326d7ac27f66 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -1507,10 +1507,10 @@ class RenderingServer : public Object {
virtual void canvas_item_set_use_parent_material(RID p_item, bool p_enable) = 0;
- virtual void instance_item_set_shader_parameter(RID p_instance, const StringName &, const Variant &p_value) = 0;
- virtual Variant instance_item_get_shader_parameter(RID p_instance, const StringName &) const = 0;
- virtual Variant instance_item_get_shader_parameter_default_value(RID p_instance, const StringName &) const = 0;
- virtual void instance_item_get_shader_parameter_list(RID p_instance, List *p_parameters) const = 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;
@@ -1789,7 +1789,7 @@ class RenderingServer : public Object {
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 _instance_item_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