Skip to content

Commit

Permalink
Implement 2D instance shader parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
huwpascoe committed Sep 16, 2024
1 parent ceba76c commit 49b0ac9
Show file tree
Hide file tree
Showing 12 changed files with 115 additions and 39 deletions.
18 changes: 18 additions & 0 deletions doc/classes/CanvasItem.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -557,6 +564,17 @@
Queues the [CanvasItem] to redraw. During idle time, if [CanvasItem] is visible, [constant NOTIFICATION_DRAW] is sent and [method _draw] is called. This only occurs [b]once[/b] per frame, even if this method has been called multiple times.
</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).
[b]Note:[/b] Per-instance shader uniforms are currently only available in 3D, so there is no 2D equivalent of this method.
</description>
</method>
<method name="set_notify_local_transform">
<return type="void" />
<param index="0" name="enable" type="bool" />
Expand Down
33 changes: 33 additions & 0 deletions doc/classes/RenderingServer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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].
[b]Note:[/b] Per-instance shader parameter names are case-sensitive.
</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]. Equivalent to [method CanvasItem.get_instance_shader_parameter].
</description>
</method>
<method name="canvas_item_reset_physics_interpolation">
<return type="void" />
<param index="0" name="item" type="RID" />
Expand Down Expand Up @@ -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" />
Expand Down
17 changes: 10 additions & 7 deletions scene/main/canvas_item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -585,10 +585,10 @@ bool CanvasItem::_get(const StringName &p_name, Variant &r_ret) const {

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);
RS::get_singleton()->canvas_item_get_instance_shader_parameter_list(get_canvas_item(), &pinfo);
for (PropertyInfo &pi : pinfo) {
bool has_def_value = false;
Variant def_value = RS::get_singleton()->instance_item_get_shader_parameter_default_value(get_canvas_item(), pi.name);
Variant def_value = RS::get_singleton()->canvas_item_get_instance_shader_parameter_default_value(get_canvas_item(), pi.name);
if (def_value.get_type() != Variant::NIL) {
has_def_value = true;
}
Expand Down Expand Up @@ -1135,22 +1135,22 @@ void CanvasItem::set_use_parent_material(bool p_use_parent_material) {

void CanvasItem::set_instance_shader_parameter(const StringName &p_name, const Variant &p_value) {
if (p_value.get_type() == Variant::NIL) {
Variant def_value = RS::get_singleton()->instance_item_get_shader_parameter_default_value(get_canvas_item(), p_name);
RS::get_singleton()->instance_item_set_shader_parameter(get_canvas_item(), p_name, def_value);
Variant def_value = RS::get_singleton()->canvas_item_get_instance_shader_parameter_default_value(get_canvas_item(), p_name);
RS::get_singleton()->canvas_item_set_instance_shader_parameter(get_canvas_item(), p_name, def_value);
instance_shader_parameters.erase(p_value);
} else {
instance_shader_parameters[p_name] = p_value;
if (p_value.get_type() == Variant::OBJECT) {
RID tex_id = p_value;
RS::get_singleton()->instance_item_set_shader_parameter(get_canvas_item(), p_name, tex_id);
RS::get_singleton()->canvas_item_set_instance_shader_parameter(get_canvas_item(), p_name, tex_id);
} else {
RS::get_singleton()->instance_item_set_shader_parameter(get_canvas_item(), p_name, p_value);
RS::get_singleton()->canvas_item_set_instance_shader_parameter(get_canvas_item(), p_name, p_value);
}
}
}

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

bool CanvasItem::get_use_parent_material() const {
Expand Down Expand Up @@ -1315,6 +1315,9 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_material", "material"), &CanvasItem::set_material);
ClassDB::bind_method(D_METHOD("get_material"), &CanvasItem::get_material);

ClassDB::bind_method(D_METHOD("set_instance_shader_parameter", "name", "value"), &CanvasItem::set_instance_shader_parameter);
ClassDB::bind_method(D_METHOD("get_instance_shader_parameter", "name"), &CanvasItem::get_instance_shader_parameter);

ClassDB::bind_method(D_METHOD("set_use_parent_material", "enable"), &CanvasItem::set_use_parent_material);
ClassDB::bind_method(D_METHOD("get_use_parent_material"), &CanvasItem::get_use_parent_material);

Expand Down
38 changes: 26 additions & 12 deletions servers/rendering/renderer_canvas_cull.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1887,7 +1887,7 @@ void RendererCanvasCull::_update_instance_shader_uniforms_from_material(HashMap<
}
}

void RendererCanvasCull::instance_item_set_shader_parameter(RID p_item, const StringName &p_parameter, const Variant &p_value) {
void RendererCanvasCull::canvas_item_set_instance_shader_parameter(RID p_item, const StringName &p_parameter, const Variant &p_value) {
Item *item = canvas_item_owner.get_or_null(p_item);
ERR_FAIL_COND(!item);

Expand All @@ -1910,8 +1910,8 @@ void RendererCanvasCull::instance_item_set_shader_parameter(RID p_item, const St
}
}

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

if (item->instance_shader_uniforms.has(p_parameter)) {
Expand All @@ -1920,8 +1920,8 @@ Variant RendererCanvasCull::instance_item_get_shader_parameter(RID p_instance, c
return Variant();
}

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

if (item->instance_shader_uniforms.has(p_parameter)) {
Expand All @@ -1930,8 +1930,8 @@ Variant RendererCanvasCull::instance_item_get_shader_parameter_default_value(RID
return Variant();
}

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

const_cast<RendererCanvasCull *>(this)->update_dirty_instances();
Expand Down Expand Up @@ -2491,7 +2491,22 @@ void RendererCanvasCull::update_dirty_instances() {

for (const KeyValue<StringName, Item::InstanceShaderParameter> &E : item->instance_shader_uniforms) {
if (E.value.value.get_type() != Variant::NIL) {
RSG::material_storage->global_shader_parameters_instance_update(item->self, E.value.index, E.value.value);
int flags_count = 0;
if (E.value.info.hint == PROPERTY_HINT_FLAGS) {
// A small hack to detect boolean flags count and prevent overhead.
switch (E.value.info.hint_string.length()) {
case 3: // "x,y"
flags_count = 1;
break;
case 5: // "x,y,z"
flags_count = 2;
break;
case 7: // "x,y,z,w"
flags_count = 3;
break;
}
}
RSG::material_storage->global_shader_parameters_instance_update(item->self, E.value.index, E.value.value, flags_count);
}
}
} else {
Expand Down Expand Up @@ -2565,11 +2580,10 @@ bool RendererCanvasCull::free(RID p_rid) {
visibility_notifier_allocator.free(canvas_item->visibility_notifier);
}

/*
if (canvas_item->material) {
canvas_item->material->owners.erase(canvas_item);
if (canvas_item->instance_allocated_shader_uniforms) {
RSG::material_storage->global_shader_parameters_instance_free(canvas_item->self);
}
*/
update_dirty_instances();

if (canvas_item->canvas_group != nullptr) {
memdelete(canvas_item->canvas_group);
Expand Down
9 changes: 4 additions & 5 deletions servers/rendering/renderer_canvas_cull.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ class RendererCanvasCull {

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() :
Expand Down Expand Up @@ -280,10 +279,10 @@ class RendererCanvasCull {
void canvas_item_set_use_parent_material(RID p_item, bool p_enable);

void _update_instance_shader_uniforms_from_material(HashMap<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_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);

Expand Down
2 changes: 2 additions & 0 deletions servers/rendering/renderer_canvas_render.h
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,8 @@ class RendererCanvasRender {
RID material;
RID skeleton;

int32_t instance_allocated_shader_uniforms_offset = -1;

Item *next = nullptr;

struct CopyBackBuffer {
Expand Down
3 changes: 3 additions & 0 deletions servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1828,6 +1828,7 @@ RendererCanvasRenderRD::RendererCanvasRenderRD() {
actions.base_varying_index = 5;

actions.global_buffer_array_variable = "global_shader_uniforms.data";
actions.instance_uniform_index_variable = "draw_data.instance_uniforms_ofs";

shader.compiler.initialize(actions);
}
Expand Down Expand Up @@ -2343,6 +2344,8 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
instance_data->color_texture_pixel_size[1] = r_current_batch->tex_texpixel_size.height;
instance_data->specular_shininess = r_current_batch->tex_specular_shininess;

instance_data->instance_uniforms_ofs = uint32_t(p_item->instance_allocated_shader_uniforms_offset);

return instance_data;
};

Expand Down
2 changes: 2 additions & 0 deletions servers/rendering/renderer_rd/renderer_canvas_render_rd.h
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,8 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
};
float color_texture_pixel_size[2];
uint32_t lights[4];
uint32_t instance_uniforms_ofs;
uint32_t pad2[3];
};

struct PushConstant {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ struct InstanceData {
#endif
vec2 color_texture_pixel_size;
uint lights[4];
uint instance_uniforms_ofs;
uint pad2[3];
};

layout(set = 4, binding = 0, std430) restrict readonly buffer DrawData {
Expand Down
8 changes: 4 additions & 4 deletions servers/rendering/rendering_server_default.h
Original file line number Diff line number Diff line change
Expand Up @@ -923,10 +923,10 @@ class RenderingServerDefault : public RenderingServer {

FUNC2(canvas_item_set_material, RID, RID)

FUNC3(instance_item_set_shader_parameter, RID, const StringName &, const Variant &)
FUNC2RC(Variant, instance_item_get_shader_parameter, RID, const StringName &)
FUNC2RC(Variant, instance_item_get_shader_parameter_default_value, RID, const StringName &)
FUNC2C(instance_item_get_shader_parameter_list, RID, List<PropertyInfo> *)
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)

Expand Down
12 changes: 6 additions & 6 deletions servers/rendering_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2050,9 +2050,9 @@ 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 {
TypedArray<Dictionary> RenderingServer::_canvas_item_get_instance_shader_parameter_list(RID p_instance) const {
List<PropertyInfo> params;
instance_item_get_shader_parameter_list(p_instance, &params);
canvas_item_get_instance_shader_parameter_list(p_instance, &params);
return convert_property_list(&params);
}

Expand Down Expand Up @@ -3276,10 +3276,10 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("canvas_item_set_material", "item", "material"), &RenderingServer::canvas_item_set_material);
ClassDB::bind_method(D_METHOD("canvas_item_set_use_parent_material", "item", "enabled"), &RenderingServer::canvas_item_set_use_parent_material);

ClassDB::bind_method(D_METHOD("instance_item_set_shader_parameter", "instance", "parameter", "value"), &RenderingServer::instance_item_set_shader_parameter);
ClassDB::bind_method(D_METHOD("instance_item_get_shader_parameter", "instance", "parameter"), &RenderingServer::instance_item_get_shader_parameter);
ClassDB::bind_method(D_METHOD("instance_item_get_shader_parameter_default_value", "instance", "parameter"), &RenderingServer::instance_item_get_shader_parameter_default_value);
ClassDB::bind_method(D_METHOD("instance_item_get_shader_parameter_list", "instance"), &RenderingServer::_instance_item_get_shader_parameter_list);
ClassDB::bind_method(D_METHOD("canvas_item_set_instance_shader_parameter", "instance", "parameter", "value"), &RenderingServer::canvas_item_set_instance_shader_parameter);
ClassDB::bind_method(D_METHOD("canvas_item_get_instance_shader_parameter", "instance", "parameter"), &RenderingServer::canvas_item_get_instance_shader_parameter);
ClassDB::bind_method(D_METHOD("canvas_item_get_instance_shader_parameter_default_value", "instance", "parameter"), &RenderingServer::canvas_item_get_instance_shader_parameter_default_value);
ClassDB::bind_method(D_METHOD("canvas_item_get_instance_shader_parameter_list", "instance"), &RenderingServer::_canvas_item_get_instance_shader_parameter_list);

ClassDB::bind_method(D_METHOD("canvas_item_set_visibility_notifier", "item", "enable", "area", "enter_callable", "exit_callable"), &RenderingServer::canvas_item_set_visibility_notifier);
ClassDB::bind_method(D_METHOD("canvas_item_set_canvas_group_mode", "item", "mode", "clear_margin", "fit_empty", "fit_margin", "blur_mipmaps"), &RenderingServer::canvas_item_set_canvas_group_mode, DEFVAL(5.0), DEFVAL(false), DEFVAL(0.0), DEFVAL(false));
Expand Down
10 changes: 5 additions & 5 deletions servers/rendering_server.h
Original file line number Diff line number Diff line change
Expand Up @@ -1507,10 +1507,10 @@ class RenderingServer : public Object {

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

virtual void instance_item_set_shader_parameter(RID p_instance, const StringName &, const Variant &p_value) = 0;
virtual Variant instance_item_get_shader_parameter(RID p_instance, const StringName &) const = 0;
virtual Variant instance_item_get_shader_parameter_default_value(RID p_instance, const StringName &) const = 0;
virtual void instance_item_get_shader_parameter_list(RID p_instance, List<PropertyInfo> *p_parameters) const = 0;
virtual void canvas_item_set_instance_shader_parameter(RID p_item, const StringName &, const Variant &p_value) = 0;
virtual Variant canvas_item_get_instance_shader_parameter(RID p_item, const StringName &) const = 0;
virtual Variant canvas_item_get_instance_shader_parameter_default_value(RID p_item, const StringName &) const = 0;
virtual void canvas_item_get_instance_shader_parameter_list(RID p_item, List<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;

Expand Down Expand Up @@ -1789,7 +1789,7 @@ class RenderingServer : public Object {
void _mesh_add_surface(RID p_mesh, const Dictionary &p_surface);
Dictionary _mesh_get_surface(RID p_mesh, int p_idx);
TypedArray<Dictionary> _instance_geometry_get_shader_parameter_list(RID p_instance) const;
TypedArray<Dictionary> _instance_item_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
Expand Down

0 comments on commit 49b0ac9

Please sign in to comment.