Implement 2D instance shader parameters

Co-authored-by: kobewi <kobewi4e@gmail.com>
Co-authored-by: yesfish <huwpascoe@users.noreply.github.com>
Co-authored-by: Álex Román Núñez <eirexe123@gmail.com>
This commit is contained in:
kobewi 2023-05-29 00:23:00 +02:00 committed by Patrick Exner
parent b9437c3938
commit ceefc0d38a
24 changed files with 626 additions and 168 deletions

View file

@ -461,6 +461,13 @@
Returns the transform from the local coordinate system of this [CanvasItem] to the [Viewport]s coordinate system.
</description>
</method>
<method name="get_instance_shader_parameter" qualifiers="const">
<return type="Variant" />
<param index="0" name="name" type="StringName" />
<description>
Get the value of a shader parameter as set on this instance.
</description>
</method>
<method name="get_local_mouse_position" qualifiers="const">
<return type="Vector2" />
<description>
@ -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.
</description>
</method>
<method name="set_instance_shader_parameter">
<return type="void" />
<param index="0" name="name" type="StringName" />
<param index="1" name="value" type="Variant" />
<description>
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).
</description>
</method>
<method name="set_notify_local_transform">
<return type="void" />
<param index="0" name="enable" type="bool" />

View file

@ -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.
</description>
</method>
</methods>

View file

@ -436,6 +436,30 @@
[b]Note:[/b] The equivalent node is [CanvasItem].
</description>
</method>
<method name="canvas_item_get_instance_shader_parameter" qualifiers="const">
<return type="Variant" />
<param index="0" name="instance" type="RID" />
<param index="1" name="parameter" type="StringName" />
<description>
Returns the value of the per-instance shader uniform from the specified canvas item instance. Equivalent to [method CanvasItem.get_instance_shader_parameter].
</description>
</method>
<method name="canvas_item_get_instance_shader_parameter_default_value" qualifiers="const">
<return type="Variant" />
<param index="0" name="instance" type="RID" />
<param index="1" name="parameter" type="StringName" />
<description>
Returns the default value of the per-instance shader uniform from the specified canvas item instance. Equivalent to [method CanvasItem.get_instance_shader_parameter].
</description>
</method>
<method name="canvas_item_get_instance_shader_parameter_list" qualifiers="const">
<return type="Dictionary[]" />
<param index="0" name="instance" type="RID" />
<description>
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].
</description>
</method>
<method name="canvas_item_reset_physics_interpolation">
<return type="void" />
<param index="0" name="item" type="RID" />
@ -524,6 +548,15 @@
Sets the index for the [CanvasItem].
</description>
</method>
<method name="canvas_item_set_instance_shader_parameter">
<return type="void" />
<param index="0" name="instance" type="RID" />
<param index="1" name="parameter" type="StringName" />
<param index="2" name="value" type="Variant" />
<description>
Sets the per-instance shader uniform on the specified canvas item instance. Equivalent to [method CanvasItem.set_instance_shader_parameter].
</description>
</method>
<method name="canvas_item_set_interpolated">
<return type="void" />
<param index="0" name="item" type="RID" />

View file

@ -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);

View file

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

View file

@ -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);

View file

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

View file

@ -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<PropertyInfo> *p_list) const {
List<PropertyInfo> 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);

View file

@ -116,6 +116,8 @@ private:
TextureRepeat texture_repeat = TEXTURE_REPEAT_PARENT_NODE;
Ref<Material> material;
mutable HashMap<StringName, Variant> instance_shader_parameters;
mutable HashMap<StringName, StringName> 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<PropertyInfo> *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<Material> &p_material);
Ref<Material> 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;

View file

@ -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<RendererMaterialStorage::InstanceShaderParam> params;
RSG::material_storage->material_get_instance_shader_parameters(p_material, &params);
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<StringName, Item> &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<PropertyInfo> &r_parameters) const {
Vector<StringName> names;
// Invalid items won't be saved, but will remain in memory in case of shader compilation failure.
for (const KeyValue<StringName, Item> &kv : _parameters) {
if (kv.value.is_valid()) {
names.push_back(kv.key);
}
}
names.sort_custom<StringName::AlphCompare>();
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<StringName, Item> &kv : _parameters) {
kv.value.index = -1;
}
}

View file

@ -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<PropertyInfo> &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<StringName, Item> _parameters;
void _init_param(Item &r_item, const RendererMaterialStorage::InstanceShaderParam &p_param) const;
void _invalidate_items();
};
#endif // INSTANCE_UNIFORMS_H

View file

@ -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<RendererCanvasCull *>(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<RendererCanvasCull *>(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<PropertyInfo> *p_parameters) const {
ERR_FAIL_NULL(p_parameters);
const Item *item = const_cast<RendererCanvasCull *>(this)->canvas_item_owner.get_or_null(p_item);
ERR_FAIL_NULL(item);
const_cast<RendererCanvasCull *>(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;
}

View file

@ -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<Item *>::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<Item> 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<Item>::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<PropertyInfo> *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);

View file

@ -343,6 +343,8 @@ public:
RID material;
RID skeleton;
int32_t instance_allocated_shader_uniforms_offset = -1;
Item *next = nullptr;
struct CopyBackBuffer {

View file

@ -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<uint32_t>(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;

View file

@ -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();

View file

@ -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];

View file

@ -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<StringName, Instance::InstanceShaderParameter>::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<PropertyInfo> *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<StringName> names;
for (const KeyValue<StringName, Instance::InstanceShaderParameter> &E : instance->instance_shader_uniforms) {
names.push_back(E.key);
}
names.sort_custom<StringName::AlphCompare>();
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<StringName, Instance::InstanceShaderParameter> &isparams, const HashMap<StringName, Instance::InstanceShaderParameter> &existing_isparams, RID p_material) const {
List<RendererMaterialStorage::InstanceShaderParam> 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<StringName, Instance::InstanceShaderParameter> 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<StringName, Instance::InstanceShaderParameter> &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);

View file

@ -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<StringName, InstanceShaderParameter> 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<StringName, Instance::InstanceShaderParameter> &isparams, const HashMap<StringName, Instance::InstanceShaderParameter> &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<PropertyInfo> *p_parameters) const;
virtual Variant instance_geometry_get_shader_parameter(RID p_instance, const StringName &p_parameter) const;

View file

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

View file

@ -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<PropertyInfo> *)
FUNC2(canvas_item_set_use_parent_material, RID, bool)
FUNC5(canvas_item_set_visibility_notifier, RID, bool, const Rect2 &, const Callable &, const Callable &)

View file

@ -9143,7 +9143,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &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;
}

View file

@ -2052,6 +2052,12 @@ TypedArray<Dictionary> RenderingServer::_instance_geometry_get_shader_parameter_
return convert_property_list(&params);
}
TypedArray<Dictionary> RenderingServer::_canvas_item_get_instance_shader_parameter_list(RID p_instance) const {
List<PropertyInfo> params;
canvas_item_get_instance_shader_parameter_list(p_instance, &params);
return convert_property_list(&params);
}
TypedArray<Image> RenderingServer::_bake_render_uv2(RID p_base, const TypedArray<RID> &p_material_overrides, const Size2i &p_image_size) {
TypedArray<RID> 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));

View file

@ -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<PropertyInfo> *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<Dictionary> _instance_geometry_get_shader_parameter_list(RID p_instance) const;
TypedArray<Dictionary> _canvas_item_get_instance_shader_parameter_list(RID p_item) const;
TypedArray<Image> _bake_render_uv2(RID p_base, const TypedArray<RID> &p_material_overrides, const Size2i &p_image_size);
void _particles_set_trail_bind_poses(RID p_particles, const TypedArray<Transform3D> &p_bind_poses);
#ifdef TOOLS_ENABLED