2
0
Эх сурвалжийг харах

Merge pull request #80106 from lawnjelly/hier_multimesh_culling

[3.x] Fix 2D MultiMesh hierarchical culling
Rémi Verschelde 2 жил өмнө
parent
commit
39ed0811e1

+ 1 - 0
drivers/dummy/rasterizer_dummy.h

@@ -455,6 +455,7 @@ public:
 	AABB _multimesh_get_aabb(RID p_multimesh) const { return AABB(); }
 	AABB _multimesh_get_aabb(RID p_multimesh) const { return AABB(); }
 
 
 	MMInterpolator *_multimesh_get_interpolator(RID p_multimesh) const { return nullptr; }
 	MMInterpolator *_multimesh_get_interpolator(RID p_multimesh) const { return nullptr; }
+	void multimesh_attach_canvas_item(RID p_multimesh, RID p_canvas_item, bool p_attach) {}
 
 
 	/* IMMEDIATE API */
 	/* IMMEDIATE API */
 
 

+ 35 - 1
drivers/gles2/rasterizer_storage_gles2.cpp

@@ -3388,6 +3388,24 @@ RasterizerStorage::MMInterpolator *RasterizerStorageGLES2::_multimesh_get_interp
 	return &multimesh->interpolator;
 	return &multimesh->interpolator;
 }
 }
 
 
+void RasterizerStorageGLES2::multimesh_attach_canvas_item(RID p_multimesh, RID p_canvas_item, bool p_attach) {
+	MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh);
+	ERR_FAIL_NULL(multimesh);
+	ERR_FAIL_COND(!p_canvas_item.is_valid());
+
+	if (p_attach) {
+		int64_t found = multimesh->linked_canvas_items.find(p_canvas_item);
+		if (found == -1) {
+			multimesh->linked_canvas_items.push_back(p_canvas_item);
+		}
+	} else {
+		int64_t found = multimesh->linked_canvas_items.find(p_canvas_item);
+		if (found != -1) {
+			multimesh->linked_canvas_items.remove_unordered(found);
+		}
+	}
+}
+
 void RasterizerStorageGLES2::update_dirty_multimeshes() {
 void RasterizerStorageGLES2::update_dirty_multimeshes() {
 	while (multimesh_update_list.first()) {
 	while (multimesh_update_list.first()) {
 		MultiMesh *multimesh = multimesh_update_list.first()->self();
 		MultiMesh *multimesh = multimesh_update_list.first()->self();
@@ -3457,6 +3475,14 @@ void RasterizerStorageGLES2::update_dirty_multimeshes() {
 			}
 			}
 
 
 			multimesh->aabb = aabb;
 			multimesh->aabb = aabb;
+
+			// Inform any linked canvas items that bounds have changed
+			// (for hierarchical culling).
+			int num_linked = multimesh->linked_canvas_items.size();
+			for (int n = 0; n < num_linked; n++) {
+				const RID &rid = multimesh->linked_canvas_items[n];
+				VSG::canvas->_canvas_item_invalidate_local_bound(rid);
+			}
 		}
 		}
 
 
 		multimesh->dirty_aabb = false;
 		multimesh->dirty_aabb = false;
@@ -4128,7 +4154,7 @@ void RasterizerStorageGLES2::update_dirty_skeletons() {
 		int num_linked = skeleton->linked_canvas_items.size();
 		int num_linked = skeleton->linked_canvas_items.size();
 		for (int n = 0; n < num_linked; n++) {
 		for (int n = 0; n < num_linked; n++) {
 			const RID &rid = skeleton->linked_canvas_items[n];
 			const RID &rid = skeleton->linked_canvas_items[n];
-			VSG::canvas->_canvas_item_skeleton_moved(rid);
+			VSG::canvas->_canvas_item_invalidate_local_bound(rid);
 		}
 		}
 
 
 		ele = ele->next();
 		ele = ele->next();
@@ -6052,6 +6078,14 @@ bool RasterizerStorageGLES2::free(RID p_rid) {
 		_interpolation_data.notify_free_multimesh(p_rid);
 		_interpolation_data.notify_free_multimesh(p_rid);
 
 
 		MultiMesh *multimesh = multimesh_owner.get(p_rid);
 		MultiMesh *multimesh = multimesh_owner.get(p_rid);
+
+		// remove any references in linked canvas items
+		int num_linked = multimesh->linked_canvas_items.size();
+		for (int n = 0; n < num_linked; n++) {
+			const RID &rid = multimesh->linked_canvas_items[n];
+			VSG::canvas->_canvas_item_remove_references(rid, p_rid);
+		}
+
 		multimesh->instance_remove_deps();
 		multimesh->instance_remove_deps();
 
 
 		if (multimesh->mesh.is_valid()) {
 		if (multimesh->mesh.is_valid()) {

+ 2 - 0
drivers/gles2/rasterizer_storage_gles2.h

@@ -789,6 +789,7 @@ public:
 		bool dirty_data;
 		bool dirty_data;
 
 
 		MMInterpolator interpolator;
 		MMInterpolator interpolator;
+		LocalVector<RID> linked_canvas_items;
 
 
 		MultiMesh() :
 		MultiMesh() :
 				size(0),
 				size(0),
@@ -835,6 +836,7 @@ public:
 
 
 	virtual AABB _multimesh_get_aabb(RID p_multimesh) const;
 	virtual AABB _multimesh_get_aabb(RID p_multimesh) const;
 	virtual MMInterpolator *_multimesh_get_interpolator(RID p_multimesh) const;
 	virtual MMInterpolator *_multimesh_get_interpolator(RID p_multimesh) const;
+	virtual void multimesh_attach_canvas_item(RID p_multimesh, RID p_canvas_item, bool p_attach);
 
 
 	void update_dirty_multimeshes();
 	void update_dirty_multimeshes();
 
 

+ 36 - 2
drivers/gles3/rasterizer_storage_gles3.cpp

@@ -4923,6 +4923,24 @@ RasterizerStorage::MMInterpolator *RasterizerStorageGLES3::_multimesh_get_interp
 	return &multimesh->interpolator;
 	return &multimesh->interpolator;
 }
 }
 
 
+void RasterizerStorageGLES3::multimesh_attach_canvas_item(RID p_multimesh, RID p_canvas_item, bool p_attach) {
+	MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh);
+	ERR_FAIL_NULL(multimesh);
+	ERR_FAIL_COND(!p_canvas_item.is_valid());
+
+	if (p_attach) {
+		int64_t found = multimesh->linked_canvas_items.find(p_canvas_item);
+		if (found == -1) {
+			multimesh->linked_canvas_items.push_back(p_canvas_item);
+		}
+	} else {
+		int64_t found = multimesh->linked_canvas_items.find(p_canvas_item);
+		if (found != -1) {
+			multimesh->linked_canvas_items.remove_unordered(found);
+		}
+	}
+}
+
 void RasterizerStorageGLES3::update_dirty_multimeshes() {
 void RasterizerStorageGLES3::update_dirty_multimeshes() {
 	while (multimesh_update_list.first()) {
 	while (multimesh_update_list.first()) {
 		MultiMesh *multimesh = multimesh_update_list.first()->self();
 		MultiMesh *multimesh = multimesh_update_list.first()->self();
@@ -5001,6 +5019,14 @@ void RasterizerStorageGLES3::update_dirty_multimeshes() {
 			}
 			}
 
 
 			multimesh->aabb = aabb;
 			multimesh->aabb = aabb;
+
+			// Inform any linked canvas items that bounds have changed
+			// (for hierarchical culling).
+			int num_linked = multimesh->linked_canvas_items.size();
+			for (int n = 0; n < num_linked; n++) {
+				const RID &rid = multimesh->linked_canvas_items[n];
+				VSG::canvas->_canvas_item_invalidate_local_bound(rid);
+			}
 		}
 		}
 		multimesh->dirty_aabb = false;
 		multimesh->dirty_aabb = false;
 		multimesh->dirty_data = false;
 		multimesh->dirty_data = false;
@@ -5348,7 +5374,7 @@ void RasterizerStorageGLES3::update_dirty_skeletons() {
 		int num_linked = skeleton->linked_canvas_items.size();
 		int num_linked = skeleton->linked_canvas_items.size();
 		for (int n = 0; n < num_linked; n++) {
 		for (int n = 0; n < num_linked; n++) {
 			const RID &rid = skeleton->linked_canvas_items[n];
 			const RID &rid = skeleton->linked_canvas_items[n];
-			VSG::canvas->_canvas_item_skeleton_moved(rid);
+			VSG::canvas->_canvas_item_invalidate_local_bound(rid);
 		}
 		}
 
 
 		ele = ele->next();
 		ele = ele->next();
@@ -7865,8 +7891,16 @@ bool RasterizerStorageGLES3::free(RID p_rid) {
 		// remove from interpolator
 		// remove from interpolator
 		_interpolation_data.notify_free_multimesh(p_rid);
 		_interpolation_data.notify_free_multimesh(p_rid);
 
 
-		// delete the texture
 		MultiMesh *multimesh = multimesh_owner.get(p_rid);
 		MultiMesh *multimesh = multimesh_owner.get(p_rid);
+
+		// remove any references in linked canvas items
+		int num_linked = multimesh->linked_canvas_items.size();
+		for (int n = 0; n < num_linked; n++) {
+			const RID &rid = multimesh->linked_canvas_items[n];
+			VSG::canvas->_canvas_item_remove_references(rid, p_rid);
+		}
+
+		// delete the texture
 		multimesh->instance_remove_deps();
 		multimesh->instance_remove_deps();
 
 
 		if (multimesh->mesh.is_valid()) {
 		if (multimesh->mesh.is_valid()) {

+ 2 - 0
drivers/gles3/rasterizer_storage_gles3.h

@@ -818,6 +818,7 @@ public:
 		bool dirty_data;
 		bool dirty_data;
 
 
 		MMInterpolator interpolator;
 		MMInterpolator interpolator;
+		LocalVector<RID> linked_canvas_items;
 
 
 		MultiMesh() :
 		MultiMesh() :
 				size(0),
 				size(0),
@@ -867,6 +868,7 @@ public:
 
 
 	virtual AABB _multimesh_get_aabb(RID p_multimesh) const;
 	virtual AABB _multimesh_get_aabb(RID p_multimesh) const;
 	virtual MMInterpolator *_multimesh_get_interpolator(RID p_multimesh) const;
 	virtual MMInterpolator *_multimesh_get_interpolator(RID p_multimesh) const;
+	virtual void multimesh_attach_canvas_item(RID p_multimesh, RID p_canvas_item, bool p_attach);
 
 
 	/* IMMEDIATE API */
 	/* IMMEDIATE API */
 
 

+ 25 - 0
servers/visual/rasterizer.h

@@ -395,6 +395,7 @@ public:
 	virtual void multimesh_set_visible_instances(RID p_multimesh, int p_visible);
 	virtual void multimesh_set_visible_instances(RID p_multimesh, int p_visible);
 	virtual int multimesh_get_visible_instances(RID p_multimesh) const;
 	virtual int multimesh_get_visible_instances(RID p_multimesh) const;
 	virtual AABB multimesh_get_aabb(RID p_multimesh) const;
 	virtual AABB multimesh_get_aabb(RID p_multimesh) const;
+	virtual void multimesh_attach_canvas_item(RID p_multimesh, RID p_canvas_item, bool p_attach) = 0;
 
 
 	virtual RID _multimesh_create() = 0;
 	virtual RID _multimesh_create() = 0;
 	virtual void _multimesh_allocate(RID p_multimesh, int p_instances, VS::MultimeshTransformFormat p_transform_format, VS::MultimeshColorFormat p_color_format, VS::MultimeshCustomDataFormat p_data = VS::MULTIMESH_CUSTOM_DATA_NONE) = 0;
 	virtual void _multimesh_allocate(RID p_multimesh, int p_instances, VS::MultimeshTransformFormat p_transform_format, VS::MultimeshColorFormat p_color_format, VS::MultimeshCustomDataFormat p_data = VS::MULTIMESH_CUSTOM_DATA_NONE) = 0;
@@ -817,6 +818,8 @@ public:
 				TYPE_MULTIRECT,
 				TYPE_MULTIRECT,
 			};
 			};
 
 
+			virtual bool contains_reference(const RID &p_rid) const { return false; }
+
 			Type type;
 			Type type;
 			virtual ~Command() {}
 			virtual ~Command() {}
 		};
 		};
@@ -946,7 +949,15 @@ public:
 			RID multimesh;
 			RID multimesh;
 			RID texture;
 			RID texture;
 			RID normal_map;
 			RID normal_map;
+			RID canvas_item;
+			virtual bool contains_reference(const RID &p_rid) const { return multimesh == p_rid; }
 			CommandMultiMesh() { type = TYPE_MULTIMESH; }
 			CommandMultiMesh() { type = TYPE_MULTIMESH; }
+			virtual ~CommandMultiMesh() {
+				// Remove any backlinks from multimesh to canvas item.
+				if (multimesh.is_valid()) {
+					RasterizerStorage::base_singleton->multimesh_attach_canvas_item(multimesh, canvas_item, false);
+				}
+			}
 		};
 		};
 
 
 		struct CommandParticles : public Command {
 		struct CommandParticles : public Command {
@@ -1199,6 +1210,20 @@ public:
 			return rect;
 			return rect;
 		}
 		}
 
 
+		void remove_references(const RID &p_rid) {
+			for (int i = commands.size() - 1; i >= 0; i--) {
+				if (commands[i]->contains_reference(p_rid)) {
+					memdelete(commands[i]);
+
+					// This could possibly be unordered if occurring close
+					// to canvas_item deletion, but is
+					// unlikely to make much performance difference,
+					// and is safer.
+					commands.remove(i);
+				}
+			}
+		}
+
 		void clear() {
 		void clear() {
 			for (int i = 0; i < commands.size(); i++) {
 			for (int i = 0; i < commands.size(); i++) {
 				memdelete(commands[i]);
 				memdelete(commands[i]);

+ 17 - 1
servers/visual/visual_server_canvas.cpp

@@ -1421,10 +1421,17 @@ void VisualServerCanvas::canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p
 	mm->multimesh = p_mesh;
 	mm->multimesh = p_mesh;
 	mm->texture = p_texture;
 	mm->texture = p_texture;
 	mm->normal_map = p_normal_map;
 	mm->normal_map = p_normal_map;
+	mm->canvas_item = p_item;
 
 
 	canvas_item->rect_dirty = true;
 	canvas_item->rect_dirty = true;
 	canvas_item->commands.push_back(mm);
 	canvas_item->commands.push_back(mm);
 	_make_bound_dirty(canvas_item);
 	_make_bound_dirty(canvas_item);
+
+	// Attach to multimesh a backlink to enable updating
+	// the canvas item local bound when the multimesh changes.
+	if (p_mesh.is_valid()) {
+		VSG::storage->multimesh_attach_canvas_item(p_mesh, p_item, true);
+	}
 }
 }
 
 
 void VisualServerCanvas::canvas_item_add_clip_ignore(RID p_item, bool p_ignore) {
 void VisualServerCanvas::canvas_item_add_clip_ignore(RID p_item, bool p_ignore) {
@@ -1588,7 +1595,16 @@ void VisualServerCanvas::canvas_item_attach_skeleton(RID p_item, RID p_skeleton)
 	}
 	}
 }
 }
 
 
-void VisualServerCanvas::_canvas_item_skeleton_moved(RID p_item) {
+// Canvas items may contain references to other resources (such as MultiMesh).
+// If the resources are deleted first, and the canvas_item retains references, it
+// will crash / error when it tries to access these.
+void VisualServerCanvas::_canvas_item_remove_references(RID p_item, RID p_rid) {
+	Item *canvas_item = canvas_item_owner.getornull(p_item);
+	ERR_FAIL_COND(!canvas_item);
+	canvas_item->remove_references(p_rid);
+}
+
+void VisualServerCanvas::_canvas_item_invalidate_local_bound(RID p_item) {
 	Item *canvas_item = canvas_item_owner.getornull(p_item);
 	Item *canvas_item = canvas_item_owner.getornull(p_item);
 	ERR_FAIL_COND(!canvas_item);
 	ERR_FAIL_COND(!canvas_item);
 	_make_bound_dirty(canvas_item);
 	_make_bound_dirty(canvas_item);

+ 3 - 1
servers/visual/visual_server_canvas.h

@@ -256,7 +256,6 @@ public:
 	void canvas_item_set_use_parent_material(RID p_item, bool p_enable);
 	void canvas_item_set_use_parent_material(RID p_item, bool p_enable);
 
 
 	void canvas_item_attach_skeleton(RID p_item, RID p_skeleton);
 	void canvas_item_attach_skeleton(RID p_item, RID p_skeleton);
-	void _canvas_item_skeleton_moved(RID p_item);
 	void canvas_item_set_skeleton_relative_xform(RID p_item, Transform2D p_relative_xform);
 	void canvas_item_set_skeleton_relative_xform(RID p_item, Transform2D p_relative_xform);
 	Rect2 _debug_canvas_item_get_rect(RID p_item);
 	Rect2 _debug_canvas_item_get_rect(RID p_item);
 	Rect2 _debug_canvas_item_get_local_bound(RID p_item);
 	Rect2 _debug_canvas_item_get_local_bound(RID p_item);
@@ -265,6 +264,9 @@ public:
 	void canvas_item_reset_physics_interpolation(RID p_item);
 	void canvas_item_reset_physics_interpolation(RID p_item);
 	void canvas_item_transform_physics_interpolation(RID p_item, Transform2D p_transform);
 	void canvas_item_transform_physics_interpolation(RID p_item, Transform2D p_transform);
 
 
+	void _canvas_item_invalidate_local_bound(RID p_item);
+	void _canvas_item_remove_references(RID p_item, RID p_rid);
+
 	RID canvas_light_create();
 	RID canvas_light_create();
 	void canvas_light_attach_to_canvas(RID p_light, RID p_canvas);
 	void canvas_light_attach_to_canvas(RID p_light, RID p_canvas);
 	void canvas_light_set_enabled(RID p_light, bool p_enabled);
 	void canvas_light_set_enabled(RID p_light, bool p_enabled);