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