Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement 2D instance shader parameters #77594

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions scene/main/canvas_item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,59 @@ 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.replace("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()->instance_item_get_shader_parameter_list(get_canvas_item(), &pinfo);
for (PropertyInfo &pi : pinfo) {
bool has_def_value = false;
Variant def_value = RS::get_singleton()->instance_item_get_shader_parameter_default_value(get_canvas_item(), pi.name);
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) {
Expand Down Expand Up @@ -1004,6 +1057,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()->instance_item_get_shader_parameter_default_value(get_canvas_item(), p_name);
RS::get_singleton()->instance_item_set_shader_parameter(get_canvas_item(), p_name, def_value);
instance_shader_parameters.erase(p_value);
} else {
instance_shader_parameters[p_name] = p_value;
if (p_value.get_type() == Variant::OBJECT) {
RID tex_id = p_value;
RS::get_singleton()->instance_item_set_shader_parameter(get_canvas_item(), p_name, tex_id);
} else {
RS::get_singleton()->instance_item_set_shader_parameter(get_canvas_item(), p_name, p_value);
}
}
}

Variant CanvasItem::get_instance_shader_parameter(const StringName &p_name) const {
return RS::get_singleton()->instance_item_get_shader_parameter(get_canvas_item(), p_name);
}

bool CanvasItem::get_use_parent_material() const {
ERR_READ_THREAD_GUARD_V(false);
return use_parent_material;
Expand Down
10 changes: 10 additions & 0 deletions scene/main/canvas_item.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ class CanvasItem : public Node {
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;
Expand Down Expand Up @@ -149,8 +151,13 @@ class CanvasItem : public Node {
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;

_FORCE_INLINE_ void _notify_transform() {
if (!is_inside_tree()) {
return;
Expand Down Expand Up @@ -337,6 +344,9 @@ class CanvasItem : public Node {
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;

Expand Down
107 changes: 107 additions & 0 deletions servers/rendering/renderer_canvas_cull.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1558,6 +1558,10 @@ void RendererCanvasCull::canvas_item_clear(RID p_item) {
ERR_FAIL_COND(!canvas_item);

canvas_item->clear();

if (!canvas_item->update_item.in_list()) {
_instance_update_list.add(&canvas_item->update_item);
}
}

void RendererCanvasCull::canvas_item_set_draw_index(RID p_item, int p_index) {
Expand Down Expand Up @@ -1593,6 +1597,94 @@ void RendererCanvasCull::canvas_item_set_use_parent_material(RID p_item, bool p_
canvas_item->use_parent_material = p_enable;
}

void RendererCanvasCull::_update_instance_shader_uniforms_from_material(HashMap<StringName, Item::InstanceShaderParameter> &isparams, const HashMap<StringName, Item::InstanceShaderParameter> &existing_isparams, RID p_material) {
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; // The first one found always has priority.
}

Item::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 RendererCanvasCull::instance_item_set_shader_parameter(RID p_item, const StringName &p_parameter, const Variant &p_value) {
Item *item = canvas_item_owner.get_or_null(p_item);
ERR_FAIL_COND(!item);

ERR_FAIL_COND(p_value.get_type() == Variant::OBJECT);

HashMap<StringName, Item::InstanceShaderParameter>::Iterator E = item->instance_shader_uniforms.find(p_parameter);

if (!E) {
Item::InstanceShaderParameter isp;
isp.index = -1;
isp.info = PropertyInfo();
isp.value = p_value;
item->instance_shader_uniforms[p_parameter] = isp;
} else {
E->value.value = p_value;
if (E->value.index >= 0 && item->instance_allocated_shader_uniforms) {
// Update directly.
RSG::material_storage->global_shader_parameters_instance_update(p_item, E->value.index, p_value);
}
}
}

Variant RendererCanvasCull::instance_item_get_shader_parameter(RID p_instance, const StringName &p_parameter) const {
const Item *item = const_cast<RendererCanvasCull *>(this)->canvas_item_owner.get_or_null(p_instance);
ERR_FAIL_COND_V(!item, Variant());

if (item->instance_shader_uniforms.has(p_parameter)) {
return item->instance_shader_uniforms[p_parameter].value;
}
return Variant();
}

Variant RendererCanvasCull::instance_item_get_shader_parameter_default_value(RID p_instance, const StringName &p_parameter) const {
const Item *item = const_cast<RendererCanvasCull *>(this)->canvas_item_owner.get_or_null(p_instance);
ERR_FAIL_COND_V(!item, Variant());

if (item->instance_shader_uniforms.has(p_parameter)) {
return item->instance_shader_uniforms[p_parameter].default_value;
}
return Variant();
}

void RendererCanvasCull::instance_item_get_shader_parameter_list(RID p_instance, List<PropertyInfo> *p_parameters) const {
const Item *item = const_cast<RendererCanvasCull *>(this)->canvas_item_owner.get_or_null(p_instance);
ERR_FAIL_COND(!item);

const_cast<RendererCanvasCull *>(this)->update_dirty_instances();

Vector<StringName> names;
for (const KeyValue<StringName, Item::InstanceShaderParameter> &E : item->instance_shader_uniforms) {
names.push_back(E.key);
}
names.sort_custom<StringName::AlphCompare>();
for (int i = 0; i < names.size(); i++) {
PropertyInfo pinfo = item->instance_shader_uniforms[names[i]].info;
p_parameters->push_back(pinfo);
}
}

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) {
Item *canvas_item = canvas_item_owner.get_or_null(p_item);
ERR_FAIL_COND(!canvas_item);
Expand Down Expand Up @@ -2031,6 +2123,21 @@ void RendererCanvasCull::update_visibility_notifiers() {
}
}

void RendererCanvasCull::update_dirty_instances() {
RSG::utilities->update_dirty_resources();

while (_instance_update_list.first()) {
Item *item = _instance_update_list.first()->self();

HashMap<StringName, Item::InstanceShaderParameter> isparams;
if (item->material.is_valid()) {
_update_instance_shader_uniforms_from_material(isparams, item->instance_shader_uniforms, item->material);
}
item->instance_shader_uniforms = isparams;
_instance_update_list.remove(&item->update_item);
}
}

bool RendererCanvasCull::free(RID p_rid) {
if (canvas_owner.owns(p_rid)) {
Canvas *canvas = canvas_owner.get_or_null(p_rid);
Expand Down
24 changes: 23 additions & 1 deletion servers/rendering/renderer_canvas_cull.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,20 @@ class RendererCanvasCull {

VisibilityNotifierData *visibility_notifier = nullptr;

Item() {
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;
SelfList<Item> update_item;

Item() :
update_item(this) {
children_order_dirty = true;
E = nullptr;
z_index = 0;
Expand All @@ -90,6 +103,8 @@ class RendererCanvasCull {
}
};

SelfList<Item>::List _instance_update_list;

struct ItemIndexSort {
_FORCE_INLINE_ bool operator()(const Item *p_left, const Item *p_right) const {
return p_left->index < p_right->index;
Expand Down Expand Up @@ -254,6 +269,12 @@ class RendererCanvasCull {

void canvas_item_set_use_parent_material(RID p_item, bool p_enable);

void _update_instance_shader_uniforms_from_material(HashMap<StringName, Item::InstanceShaderParameter> &isparams, const HashMap<StringName, Item::InstanceShaderParameter> &existing_isparams, RID p_material);
void instance_item_set_shader_parameter(RID p_item, const StringName &p_parameter, const Variant &p_value);
void instance_item_get_shader_parameter_list(RID p_instance, List<PropertyInfo> *p_parameters) const;
Variant instance_item_get_shader_parameter(RID p_instance, const StringName &p_parameter) const;
Variant instance_item_get_shader_parameter_default_value(RID p_instance, const StringName &p_parameter) const;

void canvas_item_set_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);
Expand Down Expand Up @@ -316,6 +337,7 @@ class RendererCanvasCull {
void canvas_item_set_default_texture_repeat(RID p_item, RS::CanvasItemTextureRepeat p_repeat);

void update_visibility_notifiers();
void update_dirty_instances();

bool free(RID p_rid);
RendererCanvasCull();
Expand Down
5 changes: 5 additions & 0 deletions servers/rendering/rendering_server_default.h
Original file line number Diff line number Diff line change
Expand Up @@ -878,6 +878,11 @@ class RenderingServerDefault : public RenderingServer {

FUNC2(canvas_item_set_material, RID, RID)

FUNC3(instance_item_set_shader_parameter, RID, const StringName &, const Variant &)
FUNC2RC(Variant, instance_item_get_shader_parameter, RID, const StringName &)
FUNC2RC(Variant, instance_item_get_shader_parameter_default_value, RID, const StringName &)
FUNC2C(instance_item_get_shader_parameter_list, RID, List<PropertyInfo> *)

FUNC2(canvas_item_set_use_parent_material, RID, bool)

FUNC5(canvas_item_set_visibility_notifier, RID, bool, const Rect2 &, const Callable &, const Callable &)
Expand Down
2 changes: 1 addition & 1 deletion servers/rendering/shader_language.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8322,7 +8322,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
}
}
#endif // DEBUG_ENABLED
if (String(shader_type_identifier) != "spatial") {
if (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;
}
Expand Down
11 changes: 11 additions & 0 deletions servers/rendering_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1646,6 +1646,12 @@ TypedArray<Dictionary> RenderingServer::_instance_geometry_get_shader_parameter_
return convert_property_list(&params);
}

TypedArray<Dictionary> RenderingServer::_instance_item_get_shader_parameter_list(RID p_instance) const {
List<PropertyInfo> params;
instance_item_get_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++) {
Expand Down Expand Up @@ -2632,6 +2638,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("instance_item_set_shader_parameter", "instance", "parameter", "value"), &RenderingServer::instance_item_set_shader_parameter);
ClassDB::bind_method(D_METHOD("instance_item_get_shader_parameter", "instance", "parameter"), &RenderingServer::instance_item_get_shader_parameter);
ClassDB::bind_method(D_METHOD("instance_item_get_shader_parameter_default_value", "instance", "parameter"), &RenderingServer::instance_item_get_shader_parameter_default_value);
ClassDB::bind_method(D_METHOD("instance_item_get_shader_parameter_list", "instance"), &RenderingServer::_instance_item_get_shader_parameter_list);

ClassDB::bind_method(D_METHOD("canvas_item_set_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));

Expand Down
6 changes: 6 additions & 0 deletions servers/rendering_server.h
Original file line number Diff line number Diff line change
Expand Up @@ -1378,6 +1378,11 @@ class RenderingServer : public Object {

virtual void canvas_item_set_use_parent_material(RID p_item, bool p_enable) = 0;

virtual void instance_item_set_shader_parameter(RID p_instance, const StringName &, const Variant &p_value) = 0;
virtual Variant instance_item_get_shader_parameter(RID p_instance, const StringName &) const = 0;
virtual Variant instance_item_get_shader_parameter_default_value(RID p_instance, const StringName &) const = 0;
virtual void instance_item_get_shader_parameter_list(RID p_instance, List<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 {
Expand Down Expand Up @@ -1613,6 +1618,7 @@ class RenderingServer : public Object {
void _mesh_add_surface(RID p_mesh, const Dictionary &p_surface);
Dictionary _mesh_get_surface(RID p_mesh, int p_idx);
TypedArray<Dictionary> _instance_geometry_get_shader_parameter_list(RID p_instance) const;
TypedArray<Dictionary> _instance_item_get_shader_parameter_list(RID p_instance) 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);
};
Expand Down