diff --git a/servers/camera/camera_feed.cpp b/servers/camera/camera_feed.cpp index 32a01bedf2..65518741d5 100644 --- a/servers/camera/camera_feed.cpp +++ b/servers/camera/camera_feed.cpp @@ -196,13 +196,17 @@ void CameraFeed::set_rgb_image(const Ref &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 &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 &p_y_img, const Ref &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() {