Repository: godotengine/godot
File: scene/resources/particle_process_material.cpp (C++)

Code before:

Ref<Texture2D> ParticleProcessMaterial::get_emission_curve() const {
	return emission_curve;
}

void ParticleProcessMaterial::set_velocity_limit_curve(const Ref<Texture2D> &p_texture) {
	velocity_limit_curve = p_texture;
	Variant tex_rid = p_texture.is_valid() ? Variant(p_texture->get_rid()) : Variant();
	RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->velocity_limit_curve, tex_rid);
	_queue_shader_change();
	notify_property_list_changed();
}

Ref<Texture2D> ParticleProcessMaterial::get_velocity_limit_curve() const {
	return velocity_limit_curve;
}

bool ParticleProcessMaterial::get_particle_flag(ParticleFlags p_particle_flag) const {
	ERR_FAIL_INDEX_V(p_particle_flag, PARTICLE_FLAG_MAX, false);
	return particle_flags[p_particle_flag];
}

void ParticleProcessMaterial::set_emission_shape(EmissionShape p_shape) {
	ERR_FAIL_INDEX(p_shape, EMISSION_SHAPE_MAX);
	emission_shape = p_shape;
	notify_property_list_changed();
	_queue_shader_change();
}

void ParticleProcessMaterial::set_emission_sphere_radius(real_t p_radius) {
	emission_sphere_radius = p_radius;
	RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_sphere_radius, p_radius);
}

void ParticleProcessMaterial::set_emission_box_extents(Vector3 p_extents) {
	emission_box_extents = p_extents;
	RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_box_extents, p_extents);
}

void ParticleProcessMaterial::set_emission_point_texture(const Ref<Texture2D> &p_points) {
	emission_point_texture = p_points;
	Variant tex_rid = p_points.is_valid() ? Variant(p_points->get_rid()) : Variant();
	RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_points, tex_rid);
}

void ParticleProcessMaterial::set_emission_normal_texture(const Ref<Texture2D> &p_normals) {
	emission_normal_texture = p_normals;
	Variant tex_rid = p_normals.is_valid() ? Variant(p_normals->get_rid()) : Variant();
	RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_normal, tex_rid);
}


Review comment (performance):
I agree that technically this only needs to be emitted in the editor, although performance shouldn't be a concern.
Emitting a signal to which nothing is connected is rather cheap.
The `changed` signal already deviates from its documented behavior in many cases (see https://docs.godotengine.org/en/latest/classes/class_resource.html#class-resource-signal-changed) with many resources not emitting it at all and others for every property change. 
And emitting it only in the editor further fragments the signal's behavior across contexts, violating the principle of consistent API behavior between editor and runtime. As @Mickeon pointed out, this would be the only situation where `changed` is exclusively emitted in the editor.
Not sure what the best solution is, but maybe using another signal (like the one you used further down below, `emission_shape_changed`) would be best to clearly separate it from the `changed` signal.

Code after:

void ParticleProcessMaterial::set_velocity_limit_curve(const Ref<Texture2D> &p_texture) {
	velocity_limit_curve = p_texture;
	Variant tex_rid = p_texture.is_valid() ? Variant(p_texture->get_rid()) : Variant();
	RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->velocity_limit_curve, tex_rid);
	_queue_shader_change();
	notify_property_list_changed();
}

Ref<Texture2D> ParticleProcessMaterial::get_velocity_limit_curve() const {
	return velocity_limit_curve;
}

bool ParticleProcessMaterial::get_particle_flag(ParticleFlags p_particle_flag) const {
	ERR_FAIL_INDEX_V(p_particle_flag, PARTICLE_FLAG_MAX, false);
	return particle_flags[p_particle_flag];
}

void ParticleProcessMaterial::set_emission_shape(EmissionShape p_shape) {
	ERR_FAIL_INDEX(p_shape, EMISSION_SHAPE_MAX);
	emission_shape = p_shape;
	notify_property_list_changed();
	_queue_shader_change();
#ifdef TOOLS_ENABLED
	emit_signal("emission_shape_changed");
#endif
}

void ParticleProcessMaterial::set_emission_sphere_radius(real_t p_radius) {
	emission_sphere_radius = p_radius;
	RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_sphere_radius, p_radius);
#ifdef TOOLS_ENABLED
	emit_signal("emission_shape_changed");
#endif
}

void ParticleProcessMaterial::set_emission_box_extents(Vector3 p_extents) {
	emission_box_extents = p_extents;
	RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_box_extents, p_extents);
#ifdef TOOLS_ENABLED
	emit_signal("emission_shape_changed");
#endif
}

void ParticleProcessMaterial::set_emission_point_texture(const Ref<Texture2D> &p_points) {
	emission_point_texture = p_points;
	Variant tex_rid = p_points.is_valid() ? Variant(p_points->get_rid()) : Variant();
	RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_points, tex_rid);
}

void ParticleProcessMaterial::set_emission_normal_texture(const Ref<Texture2D> &p_normals) {