Browse Source

Merge pull request #109594 from j20001970/camera-feeds-deferred-frame-changed

Defer `format_changed` and `frame_changed` signal for all camera feeds
Thaddeus Crews 2 weeks ago
parent
commit
54a0a940bd
1 changed files with 21 additions and 7 deletions
  1. 21 7
      servers/camera/camera_feed.cpp

+ 21 - 7
servers/camera/camera_feed.cpp

@@ -196,13 +196,17 @@ void CameraFeed::set_rgb_image(const Ref<Image> &p_rgb_img) {
 			RID new_texture = RenderingServer::get_singleton()->texture_2d_create(p_rgb_img);
 			RenderingServer::get_singleton()->texture_replace(texture[CameraServer::FEED_RGBA_IMAGE], new_texture);
 
-			emit_signal(format_changed_signal_name);
+			// Defer `format_changed` signals to ensure they are emitted on Godot's main thread.
+			// This also makes sure the datatype of the feed is updated before the emission.
+			call_deferred("emit_signal", format_changed_signal_name);
 		} else {
 			RenderingServer::get_singleton()->texture_2d_update(texture[CameraServer::FEED_RGBA_IMAGE], p_rgb_img);
 		}
 
 		datatype = CameraFeed::FEED_RGB;
-		emit_signal(frame_changed_signal_name);
+		// Most of the time the pixel data of camera devices comes from threads outside Godot.
+		// Defer `frame_changed` signals to ensure they are emitted on Godot's main thread.
+		call_deferred("emit_signal", frame_changed_signal_name);
 	}
 }
 
@@ -220,13 +224,17 @@ void CameraFeed::set_ycbcr_image(const Ref<Image> &p_ycbcr_img) {
 			RID new_texture = RenderingServer::get_singleton()->texture_2d_create(p_ycbcr_img);
 			RenderingServer::get_singleton()->texture_replace(texture[CameraServer::FEED_RGBA_IMAGE], new_texture);
 
-			emit_signal(format_changed_signal_name);
+			// Defer `format_changed` signals to ensure they are emitted on Godot's main thread.
+			// This also makes sure the datatype of the feed is updated before the emission.
+			call_deferred("emit_signal", format_changed_signal_name);
 		} else {
 			RenderingServer::get_singleton()->texture_2d_update(texture[CameraServer::FEED_RGBA_IMAGE], p_ycbcr_img);
 		}
 
 		datatype = CameraFeed::FEED_YCBCR;
-		emit_signal(frame_changed_signal_name);
+		// Most of the time the pixel data of camera devices comes from threads outside Godot.
+		// Defer `frame_changed` signals to ensure they are emitted on Godot's main thread.
+		call_deferred("emit_signal", frame_changed_signal_name);
 	}
 }
 
@@ -254,14 +262,18 @@ void CameraFeed::set_ycbcr_images(const Ref<Image> &p_y_img, const Ref<Image> &p
 				RenderingServer::get_singleton()->texture_replace(texture[CameraServer::FEED_CBCR_IMAGE], new_texture);
 			}
 
-			emit_signal(format_changed_signal_name);
+			// Defer `format_changed` signals to ensure they are emitted on Godot's main thread.
+			// This also makes sure the datatype of the feed is updated before the emission.
+			call_deferred("emit_signal", format_changed_signal_name);
 		} else {
 			RenderingServer::get_singleton()->texture_2d_update(texture[CameraServer::FEED_Y_IMAGE], p_y_img);
 			RenderingServer::get_singleton()->texture_2d_update(texture[CameraServer::FEED_CBCR_IMAGE], p_cbcr_img);
 		}
 
 		datatype = CameraFeed::FEED_YCBCR_SEP;
-		emit_signal(frame_changed_signal_name);
+		// Most of the time the pixel data of camera devices comes from threads outside Godot.
+		// Defer `frame_changed` signals to ensure they are emitted on Godot's main thread.
+		call_deferred("emit_signal", frame_changed_signal_name);
 	}
 }
 
@@ -276,7 +288,9 @@ void CameraFeed::set_external(int p_width, int p_height) {
 	}
 
 	datatype = CameraFeed::FEED_EXTERNAL;
-	emit_signal(frame_changed_signal_name);
+	// Most of the time the pixel data of camera devices comes from threads outside Godot.
+	// Defer `frame_changed` signals to ensure they are emitted on Godot's main thread.
+	call_deferred("emit_signal", frame_changed_signal_name);
 }
 
 bool CameraFeed::activate_feed() {