From ceefc0d38a04fe933f65a819e20172a60fc5cf03 Mon Sep 17 00:00:00 2001 From: kobewi Date: Mon, 29 May 2023 00:23:00 +0200 Subject: [PATCH] Implement 2D instance shader parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: kobewi Co-authored-by: yesfish Co-authored-by: Álex Román Núñez --- doc/classes/CanvasItem.xml | 17 ++ doc/classes/GeometryInstance3D.xml | 2 +- doc/classes/RenderingServer.xml | 33 ++++ drivers/gles3/rasterizer_canvas_gles3.cpp | 15 +- drivers/gles3/rasterizer_canvas_gles3.h | 5 +- drivers/gles3/shaders/canvas.glsl | 9 +- drivers/gles3/storage/material_storage.cpp | 1 + scene/main/canvas_item.cpp | 77 ++++++++ scene/main/canvas_item.h | 10 + servers/rendering/instance_uniforms.cpp | 186 ++++++++++++++++++ servers/rendering/instance_uniforms.h | 73 +++++++ servers/rendering/renderer_canvas_cull.cpp | 126 +++++++++++- servers/rendering/renderer_canvas_cull.h | 31 ++- servers/rendering/renderer_canvas_render.h | 2 + .../renderer_rd/renderer_canvas_render_rd.cpp | 24 +-- .../renderer_rd/renderer_canvas_render_rd.h | 4 +- .../shaders/canvas_uniforms_inc.glsl | 2 +- servers/rendering/renderer_scene_cull.cpp | 138 ++----------- servers/rendering/renderer_scene_cull.h | 14 +- .../rendering/rendering_server_default.cpp | 1 + servers/rendering/rendering_server_default.h | 5 + servers/rendering/shader_language.cpp | 2 +- servers/rendering_server.cpp | 11 ++ servers/rendering_server.h | 6 + 24 files changed, 626 insertions(+), 168 deletions(-) create mode 100644 servers/rendering/instance_uniforms.cpp create mode 100644 servers/rendering/instance_uniforms.h 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