15#include <boost/asio/io_context.hpp>
16#include <boost/asio/ip/tcp.hpp>
17#include <boost/asio/write.hpp>
40 struct SecondaryStreams {
42 server.SetSessionsUseStreamAliases(
true);
44 registry.OpenEpisode(0u);
48 SensorStreamRegistry registry;
52 carla::Buffer buffer(boost::asio::buffer(text.c_str(), text.size()));
58 bool WriteUntilReceived(
60 const std::atomic_bool &received,
62 for (
int attempt = 0; attempt < attempts && !received; ++attempt) {
63 WriteMessage(stream,
"data");
64 std::this_thread::sleep_for(std::chrono::milliseconds(10));
69 boost::asio::ip::tcp::socket OpenRawSession(
70 boost::asio::io_context &io_context,
72 stream_id_type stream_id) {
73 boost::asio::ip::tcp::socket socket(io_context);
74 socket.connect({boost::asio::ip::make_address(
"127.0.0.1"), port});
75 boost::asio::write(socket, boost::asio::buffer(&stream_id,
sizeof(stream_id)));
81 for (
int attempt = 0; attempt < 200; ++attempt) {
82 WriteMessage(stream,
"data");
83 std::this_thread::sleep_for(std::chrono::milliseconds(10));
84 boost::system::error_code ec;
85 if (socket.available(ec) > 0u) {
92 void Drain(boost::asio::ip::tcp::socket &socket) {
93 std::array<char, 1024> buffer;
94 boost::system::error_code ec;
95 while (socket.available(ec) > 0u) {
96 socket.read_some(boost::asio::buffer(buffer), ec);
102TEST(streaming, find_token_does_not_create_a_stream_for_an_unknown_id) {
104 EXPECT_FALSE(server.
FindToken(42u).has_value());
105 EXPECT_FALSE(server.
FindToken(42u).has_value());
109TEST(streaming, find_token_returns_the_existing_stream_token) {
112 const auto token = server.
FindToken(StreamIdOf(stream));
113 ASSERT_TRUE(token.has_value());
114 EXPECT_EQ(token->get_stream_id(), StreamIdOf(stream));
117TEST(streaming, stream_alias_can_be_set_replaced_and_removed) {
121 EXPECT_EQ(server.
FindStreamAlias(100u), std::optional<stream_id_type>(3u));
123 EXPECT_EQ(server.
FindStreamAlias(100u), std::optional<stream_id_type>(4u));
128TEST(MultiGpuSensorStreams, token_carries_the_primary_stream_id_and_the_secondary_port) {
129 SecondaryStreams secondary;
130 auto sensor = secondary.server.MakeStream();
131 EXPECT_FALSE(secondary.registry.Bind(secondary.server, 7u, StreamIdOf(sensor),
true).has_value());
133 const auto token = secondary.registry.Resolve(secondary.server, 7u, 100u);
134 ASSERT_TRUE(token.has_value());
135 EXPECT_EQ(token->get_stream_id(), 100u);
136 EXPECT_EQ(token->get_port(), secondary.server.GetLocalEndpoint().port());
137 EXPECT_EQ(secondary.server.FindStreamAlias(100u), std::optional<stream_id_type>(StreamIdOf(sensor)));
140TEST(MultiGpuSensorStreams, resolve_is_keyed_by_actor_not_creation_order) {
141 SecondaryStreams secondary;
144 auto second_sensor = secondary.server.MakeStream();
145 auto first_sensor = secondary.server.MakeStream();
146 EXPECT_FALSE(secondary.registry.Bind(secondary.server, 20u, StreamIdOf(second_sensor),
true).has_value());
147 EXPECT_FALSE(secondary.registry.Bind(secondary.server, 10u, StreamIdOf(first_sensor),
true).has_value());
149 ASSERT_TRUE(secondary.registry.Resolve(secondary.server, 10u, 101u).has_value());
150 ASSERT_TRUE(secondary.registry.Resolve(secondary.server, 20u, 102u).has_value());
151 EXPECT_EQ(secondary.server.FindStreamAlias(101u), std::optional<stream_id_type>(StreamIdOf(first_sensor)));
152 EXPECT_EQ(secondary.server.FindStreamAlias(102u), std::optional<stream_id_type>(StreamIdOf(second_sensor)));
155TEST(MultiGpuSensorStreams, resolve_before_spawn_reserves_a_stream_the_sensor_adopts) {
156 SecondaryStreams secondary;
157 const auto token = secondary.registry.Resolve(secondary.server, 7u, 100u);
158 ASSERT_TRUE(token.has_value());
159 EXPECT_EQ(token->get_stream_id(), 100u);
160 const auto reserved_id = secondary.server.FindStreamAlias(100u);
161 ASSERT_TRUE(reserved_id.has_value());
163 const auto again = secondary.registry.Resolve(secondary.server, 7u, 100u);
164 ASSERT_TRUE(again.has_value());
165 EXPECT_EQ(again->get_stream_id(), 100u);
166 EXPECT_EQ(secondary.server.FindStreamAlias(100u), reserved_id);
168 auto sensor = secondary.server.MakeStream();
169 ASSERT_NE(StreamIdOf(sensor), *reserved_id);
170 const auto adopted = secondary.registry.Bind(secondary.server, 7u, StreamIdOf(sensor),
true);
171 ASSERT_TRUE(adopted.has_value());
172 EXPECT_EQ(StreamIdOf(*adopted), *reserved_id);
173 EXPECT_EQ(secondary.server.FindStreamAlias(100u), reserved_id);
176TEST(MultiGpuSensorStreams, resolve_after_the_sensor_stream_closed_reserves_a_new_one) {
177 SecondaryStreams secondary;
178 auto sensor = secondary.server.MakeStream();
179 EXPECT_FALSE(secondary.registry.Bind(secondary.server, 7u, StreamIdOf(sensor),
true).has_value());
180 ASSERT_TRUE(secondary.registry.Resolve(secondary.server, 7u, 100u).has_value());
181 secondary.server.CloseStream(StreamIdOf(sensor));
183 const auto token = secondary.registry.Resolve(secondary.server, 7u, 100u);
184 ASSERT_TRUE(token.has_value());
185 EXPECT_EQ(token->get_stream_id(), 100u);
186 const auto reserved_id = secondary.server.FindStreamAlias(100u);
187 ASSERT_TRUE(reserved_id.has_value());
188 EXPECT_NE(*reserved_id, StreamIdOf(sensor));
189 EXPECT_TRUE(secondary.server.FindToken(*reserved_id).has_value());
192TEST(MultiGpuSensorStreams, unbind_removes_the_alias_and_closes_an_unadopted_reservation) {
193 SecondaryStreams secondary;
194 ASSERT_TRUE(secondary.registry.Resolve(secondary.server, 7u, 100u).has_value());
195 const auto reserved_id = secondary.server.FindStreamAlias(100u);
196 ASSERT_TRUE(reserved_id.has_value());
198 secondary.registry.Unbind(secondary.server, 7u);
199 EXPECT_FALSE(secondary.server.FindStreamAlias(100u).has_value());
200 EXPECT_FALSE(secondary.server.FindToken(*reserved_id).has_value());
203TEST(MultiGpuSensorStreams, episode_change_drops_aliases_and_reservations_but_not_sensor_streams) {
204 SecondaryStreams secondary;
205 auto sensor = secondary.server.MakeStream();
206 EXPECT_FALSE(secondary.registry.Bind(secondary.server, 7u, StreamIdOf(sensor),
true).has_value());
207 ASSERT_TRUE(secondary.registry.Resolve(secondary.server, 7u, 100u).has_value());
208 ASSERT_TRUE(secondary.registry.Resolve(secondary.server, 8u, 101u).has_value());
209 const auto reserved_id = secondary.server.FindStreamAlias(101u);
210 ASSERT_TRUE(reserved_id.has_value());
212 static_cast<void>(secondary.registry.BeginEpisodeChange(secondary.server));
213 EXPECT_FALSE(secondary.server.FindStreamAlias(100u).has_value());
214 EXPECT_FALSE(secondary.server.FindStreamAlias(101u).has_value());
215 EXPECT_FALSE(secondary.server.FindToken(*reserved_id).has_value());
217 EXPECT_TRUE(secondary.server.FindToken(StreamIdOf(sensor)).has_value());
220TEST(MultiGpuSensorStreams, bind_and_unbind_are_ignored_until_the_latest_load_opens) {
221 SecondaryStreams secondary;
222 const auto first_load = secondary.registry.BeginEpisodeChange(secondary.server);
223 const auto second_load = secondary.registry.BeginEpisodeChange(secondary.server);
224 ASSERT_NE(first_load, second_load);
227 ASSERT_TRUE(secondary.registry.Resolve(secondary.server, 7u, 100u).has_value());
228 const auto reserved_id = secondary.server.FindStreamAlias(100u);
229 ASSERT_TRUE(reserved_id.has_value());
232 auto stale_sensor = secondary.server.MakeStream();
233 EXPECT_FALSE(secondary.registry.Bind(secondary.server, 7u, StreamIdOf(stale_sensor),
true).has_value());
234 secondary.registry.Unbind(secondary.server, 7u);
235 secondary.registry.OpenEpisode(first_load);
236 EXPECT_FALSE(secondary.registry.Bind(secondary.server, 7u, StreamIdOf(stale_sensor),
true).has_value());
237 EXPECT_EQ(secondary.server.FindStreamAlias(100u), reserved_id);
238 EXPECT_TRUE(secondary.server.FindToken(*reserved_id).has_value());
240 secondary.registry.OpenEpisode(second_load);
241 auto sensor = secondary.server.MakeStream();
242 const auto adopted = secondary.registry.Bind(secondary.server, 7u, StreamIdOf(sensor),
true);
243 ASSERT_TRUE(adopted.has_value());
244 EXPECT_EQ(StreamIdOf(*adopted), *reserved_id);
247TEST(MultiGpuSensorStreams, close_episode_stops_binding_but_keeps_reservations) {
248 SecondaryStreams secondary;
249 ASSERT_TRUE(secondary.registry.Resolve(secondary.server, 7u, 100u).has_value());
250 secondary.registry.CloseEpisode();
252 auto sensor = secondary.server.MakeStream();
253 EXPECT_FALSE(secondary.registry.Bind(secondary.server, 7u, StreamIdOf(sensor),
true).has_value());
254 EXPECT_TRUE(secondary.server.FindStreamAlias(100u).has_value());
257TEST(MultiGpuSensorStreams, secondaries_with_colliding_local_ids_hand_out_distinct_primary_ids) {
258 SecondaryStreams secondary_a;
259 SecondaryStreams secondary_b;
260 auto sensor_a = secondary_a.server.MakeStream();
261 auto sensor_b = secondary_b.server.MakeStream();
262 ASSERT_EQ(StreamIdOf(sensor_a), StreamIdOf(sensor_b));
263 EXPECT_FALSE(secondary_a.registry.Bind(secondary_a.server, 7u, StreamIdOf(sensor_a),
true).has_value());
264 EXPECT_FALSE(secondary_b.registry.Bind(secondary_b.server, 8u, StreamIdOf(sensor_b),
true).has_value());
266 const auto token_a = secondary_a.registry.Resolve(secondary_a.server, 7u, 14u);
267 const auto token_b = secondary_b.registry.Resolve(secondary_b.server, 8u, 15u);
268 ASSERT_TRUE(token_a.has_value());
269 ASSERT_TRUE(token_b.has_value());
270 EXPECT_EQ(token_a->get_stream_id(), 14u);
271 EXPECT_EQ(token_b->get_stream_id(), 15u);
274 std::atomic_bool received_a{
false};
275 std::atomic_bool received_b{
false};
278 client.
Subscribe(*token_a, [&received_a](
auto) { received_a =
true; });
279 client.
Subscribe(*token_b, [&received_b](
auto) { received_b =
true; });
280 EXPECT_TRUE(WriteUntilReceived(sensor_a, received_a));
281 EXPECT_TRUE(WriteUntilReceived(sensor_b, received_b));
284TEST(MultiGpuSensorStreams, subscription_by_primary_id_reaches_the_adopted_stream_and_unsubscribes_by_it) {
285 SecondaryStreams secondary;
286 const auto token = secondary.registry.Resolve(secondary.server, 7u, 100u);
287 ASSERT_TRUE(token.has_value());
289 std::atomic_bool received{
false};
290 std::atomic_size_t count{0u};
293 client.
Subscribe(*token, [&count, &received](
auto) {
298 auto own_stream = secondary.server.MakeStream();
299 auto adopted = secondary.registry.Bind(secondary.server, 7u, StreamIdOf(own_stream),
true);
300 ASSERT_TRUE(adopted.has_value());
301 secondary.server.CloseStream(StreamIdOf(own_stream));
302 ASSERT_TRUE(WriteUntilReceived(*adopted, received));
305 token_type primary_token(*token);
306 primary_token.set_stream_id(100u);
308 std::this_thread::sleep_for(std::chrono::milliseconds(100));
309 const auto count_after_unsubscribe = count.load();
310 for (
int i = 0; i < 20; ++i) {
311 WriteMessage(*adopted,
"data");
312 std::this_thread::sleep_for(std::chrono::milliseconds(5));
314 EXPECT_EQ(count.load(), count_after_unsubscribe);
317TEST(MultiGpuSensorStreams, subscription_by_an_unaliased_id_is_refused) {
318 SecondaryStreams secondary;
319 auto sensor = secondary.server.MakeStream();
320 EXPECT_FALSE(secondary.registry.Bind(secondary.server, 7u, StreamIdOf(sensor),
true).has_value());
321 const auto token = secondary.registry.Resolve(secondary.server, 7u, 100u);
322 ASSERT_TRUE(token.has_value());
325 token_type stale_token(*token);
326 stale_token.set_stream_id(StreamIdOf(sensor));
327 std::atomic_bool received{
false};
330 client.
Subscribe(stale_token, [&received](
auto) { received =
true; });
331 for (
int i = 0; i < 20; ++i) {
332 WriteMessage(sensor,
"data");
333 std::this_thread::sleep_for(std::chrono::milliseconds(5));
335 EXPECT_FALSE(received);
339TEST(MultiGpuSensorStreams, reused_sensor_keeps_its_stream_and_takes_over_the_reservation) {
340 SecondaryStreams secondary;
341 ASSERT_TRUE(secondary.registry.Resolve(secondary.server, 7u, 100u).has_value());
342 const auto reserved_id = secondary.server.FindStreamAlias(100u);
343 ASSERT_TRUE(reserved_id.has_value());
345 auto live_sensor = secondary.server.MakeStream();
346 EXPECT_FALSE(secondary.registry.Bind(secondary.server, 7u, StreamIdOf(live_sensor),
false).has_value());
347 EXPECT_EQ(secondary.server.FindStreamAlias(100u), std::optional<stream_id_type>(StreamIdOf(live_sensor)));
348 EXPECT_FALSE(secondary.server.FindToken(*reserved_id).has_value());
349 EXPECT_TRUE(secondary.server.FindToken(StreamIdOf(live_sensor)).has_value());
351 const auto token = secondary.registry.Resolve(secondary.server, 7u, 100u);
352 ASSERT_TRUE(token.has_value());
353 EXPECT_EQ(secondary.server.FindStreamAlias(100u), std::optional<stream_id_type>(StreamIdOf(live_sensor)));
356TEST(MultiGpuSensorStreams, early_subscriber_of_a_reused_sensor_reconnects_to_its_stream) {
357 SecondaryStreams secondary;
358 const auto token = secondary.registry.Resolve(secondary.server, 7u, 100u);
359 ASSERT_TRUE(token.has_value());
361 std::atomic_bool received{
false};
364 client.
Subscribe(*token, [&received](
auto) { received =
true; });
365 std::this_thread::sleep_for(std::chrono::milliseconds(100));
367 auto live_sensor = secondary.server.MakeStream();
368 EXPECT_FALSE(secondary.registry.Bind(secondary.server, 7u, StreamIdOf(live_sensor),
false).has_value());
369 EXPECT_TRUE(WriteUntilReceived(live_sensor, received, 500));
373TEST(MultiGpuSensorStreams, bind_refuses_a_stream_already_bound_to_another_actor) {
374 SecondaryStreams secondary;
375 auto sensor = secondary.server.MakeStream();
376 EXPECT_FALSE(secondary.registry.Bind(secondary.server, 10u, StreamIdOf(sensor),
true).has_value());
377 ASSERT_TRUE(secondary.registry.Resolve(secondary.server, 10u, 101u).has_value());
379 EXPECT_FALSE(secondary.registry.Bind(secondary.server, 20u, StreamIdOf(sensor),
false).has_value());
380 ASSERT_TRUE(secondary.registry.Resolve(secondary.server, 20u, 102u).has_value());
381 EXPECT_EQ(secondary.server.FindStreamAlias(101u), std::optional<stream_id_type>(StreamIdOf(sensor)));
382 const auto other = secondary.server.FindStreamAlias(102u);
383 ASSERT_TRUE(other.has_value());
384 EXPECT_NE(*other, StreamIdOf(sensor));
387TEST(streaming, closing_a_session_after_its_alias_moved_leaves_the_new_stream_session_alone) {
393 const auto port = token_type(old_stream.token()).
get_port();
397 boost::asio::io_context io_context;
399 auto old_session = OpenRawSession(io_context, port, 100u);
400 ASSERT_TRUE(WriteUntilReadable(old_stream, old_session));
403 auto new_session = OpenRawSession(io_context, port, 100u);
404 ASSERT_TRUE(WriteUntilReadable(new_stream, new_session));
408 for (
int i = 0; i < 20; ++i) {
409 WriteMessage(old_stream,
"data");
410 std::this_thread::sleep_for(std::chrono::milliseconds(10));
413 EXPECT_TRUE(WriteUntilReadable(new_stream, new_session));
422 struct FakeSecondary {
424 std::string blueprint;
426 bool map_registered =
false;
427 bool dormant =
false;
431 const FakeSecondary &secondary;
432 std::string blueprint;
435 bool IsSameKind(uint32_t
id)
const {
436 const auto it = secondary.actors.find(
id);
437 return (it != secondary.actors.end()) && (it->second.blueprint == blueprint);
440 std::optional<uint32_t> FindMapActor()
const {
441 if (blueprint ==
"spectator") {
442 return secondary.spectator;
444 for (
const auto &[
id, actor] : secondary.actors) {
445 if (IsMapActorLike(
id) && (secondary.place.count(
id) > 0u) && (secondary.place.at(
id) == mesh +
"@place")) {
452 bool IsMapActorLike(uint32_t
id)
const {
453 const auto it = secondary.actors.find(
id);
454 return (it != secondary.actors.end()) && it->second.map_registered &&
455 (it->second.blueprint == blueprint) && (it->second.mesh == mesh);
458 bool Contains(uint32_t
id)
const {
459 return secondary.actors.count(
id) > 0u;
463 std::map<uint32_t, Actor> actors;
464 std::map<uint32_t, std::string> place;
465 std::optional<uint32_t> spectator;
467 uint32_t counter = 0u;
469 uint32_t AddMapActor(std::string blueprint, std::string mesh = {}) {
470 const uint32_t
id = ++counter;
471 actors[id] =
Actor{std::move(blueprint), std::move(mesh),
true,
false};
481 uint32_t Replay(uint32_t primary_id,
const std::string &blueprint,
const std::string &mesh = {}) {
482 const auto plan = Plan(primary_id, blueprint, mesh);
483 uint32_t local_id = 0u;
485 local_id = *plan.reuse;
487 EXPECT_EQ(actors.count(plan.spawn_id), 0u) <<
"spawn requested taken id " << plan.spawn_id;
488 local_id = ++counter;
489 if ((plan.spawn_id != 0u) && (actors.count(plan.spawn_id) == 0u)) {
490 local_id = plan.spawn_id;
491 counter = std::max(counter, local_id);
493 actors[local_id] =
Actor{blueprint, mesh,
false,
false};
495 mirrors[primary_id] = local_id;
499 void Remove(uint32_t primary_id) {
500 actors.erase(mirrors[primary_id]);
501 mirrors.erase(primary_id);
505 bool MirrorsAreDistinct(
const MirroredActorMap &mirrors) {
506 std::set<uint32_t> locals;
507 for (
const auto &item : mirrors) {
508 if (!locals.insert(item.second).second) {
517TEST(MultiGpuMirroredActors, a_local_id_equal_to_the_primary_id_of_another_mirror_is_not_reused) {
518 FakeSecondary secondary;
519 secondary.counter = 156u;
520 secondary.actors[157u] = {
"sensor.camera.rgb", {},
false,
false};
521 secondary.mirrors[153u] = 157u;
523 const auto plan = secondary.Plan(157u,
"sensor.camera.rgb");
524 EXPECT_FALSE(plan.reuse.has_value());
525 EXPECT_EQ(plan.spawn_id, 0u);
528TEST(MultiGpuMirroredActors, restarted_secondary_gives_every_primary_camera_its_own_actor) {
531 FakeSecondary secondary;
532 secondary.counter = 146u;
533 for (
int i = 0; i < 10; ++i) {
534 secondary.AddMapActor(
"traffic.stop");
536 for (uint32_t primary_id = 153u; primary_id <= 160u; ++primary_id) {
537 secondary.Replay(primary_id,
"sensor.camera.rgb");
539 EXPECT_EQ(secondary.mirrors.size(), 8u);
540 EXPECT_TRUE(MirrorsAreDistinct(secondary.mirrors));
541 EXPECT_EQ(secondary.actors.size(), 18u);
544TEST(MultiGpuMirroredActors, an_id_held_by_a_dormant_actor_is_never_requested) {
545 FakeSecondary secondary;
546 secondary.counter = 160u;
547 secondary.actors[157u] = {
"vehicle.lincoln.mkz", {},
false,
true};
548 secondary.mirrors[153u] = 157u;
550 const auto plan = secondary.Plan(157u,
"sensor.camera.rgb");
551 EXPECT_FALSE(plan.reuse.has_value());
552 EXPECT_EQ(plan.spawn_id, 0u);
555TEST(MultiGpuMirroredActors, a_free_id_is_requested_for_a_new_actor) {
556 FakeSecondary secondary;
557 const auto plan = secondary.Plan(42u,
"vehicle.lincoln.mkz");
558 EXPECT_FALSE(plan.reuse.has_value());
559 EXPECT_EQ(plan.spawn_id, 42u);
562TEST(MultiGpuMirroredActors, a_mapped_actor_reuses_its_mirror_even_if_its_id_is_taken) {
563 FakeSecondary secondary;
564 secondary.counter = 160u;
565 secondary.actors[153u] = {
"traffic.stop", {},
true,
false};
566 secondary.actors[157u] = {
"sensor.camera.rgb", {},
false,
false};
567 secondary.mirrors[153u] = 157u;
569 EXPECT_EQ(secondary.Plan(153u,
"sensor.camera.rgb").reuse, std::optional<uint32_t>(157u));
570 EXPECT_EQ(secondary.Replay(153u,
"sensor.camera.rgb"), 157u);
571 EXPECT_EQ(secondary.actors.size(), 2u);
574TEST(MultiGpuMirroredActors, a_map_prop_at_the_primary_id_is_reused_and_mapped) {
575 FakeSecondary secondary;
576 const uint32_t prop = secondary.AddMapActor(
"static.prop.mesh",
"cone");
578 EXPECT_EQ(secondary.Replay(prop,
"static.prop.mesh",
"cone"), prop);
579 EXPECT_EQ(secondary.mirrors.at(prop), prop);
580 EXPECT_EQ(secondary.actors.size(), 1u);
581 EXPECT_EQ(secondary.Plan(prop,
"static.prop.mesh",
"cone").reuse, std::optional<uint32_t>(prop));
584TEST(MultiGpuMirroredActors, a_map_prop_at_its_place_is_reused_whatever_its_id) {
585 FakeSecondary secondary;
586 secondary.AddMapActor(
"static.prop.mesh",
"barrel");
587 const uint32_t cone = secondary.AddMapActor(
"static.prop.mesh",
"cone");
588 secondary.place[cone] =
"cone@place";
590 EXPECT_EQ(secondary.Replay(90u,
"static.prop.mesh",
"cone"), cone);
591 EXPECT_EQ(secondary.actors.size(), 2u);
594TEST(MultiGpuMirroredActors, a_map_prop_mirroring_another_primary_actor_is_not_reused) {
595 FakeSecondary secondary;
596 const uint32_t prop = secondary.AddMapActor(
"static.prop.mesh",
"cone");
597 secondary.mirrors[12u] = prop;
599 const auto plan = secondary.Plan(prop,
"static.prop.mesh",
"cone");
600 EXPECT_FALSE(plan.reuse.has_value());
601 EXPECT_EQ(plan.spawn_id, 0u);
604TEST(MultiGpuMirroredActors, a_map_prop_with_another_mesh_is_not_reused) {
605 FakeSecondary secondary;
606 const uint32_t prop = secondary.AddMapActor(
"static.prop.mesh",
"barrel");
607 EXPECT_FALSE(secondary.Plan(prop,
"static.prop.mesh",
"cone").reuse.has_value());
610TEST(MultiGpuMirroredActors, the_spectator_is_reused_whatever_its_id) {
611 FakeSecondary secondary;
612 secondary.spectator = secondary.AddMapActor(
"spectator");
614 EXPECT_EQ(secondary.Replay(7u,
"spectator"), *secondary.spectator);
615 EXPECT_EQ(secondary.actors.size(), 1u);
618TEST(MultiGpuMirroredActors, a_removed_actor_is_spawned_again_on_its_next_add) {
619 FakeSecondary secondary;
620 const uint32_t
first = secondary.Replay(30u,
"vehicle.lincoln.mkz");
621 secondary.Remove(30u);
622 EXPECT_EQ(secondary.mirrors.count(30u), 0u);
624 secondary.AddMapActor(
"traffic.stop");
625 const uint32_t second = secondary.Replay(30u,
"vehicle.lincoln.mkz");
626 EXPECT_EQ(secondary.actors.count(second), 1u);
627 EXPECT_EQ(secondary.mirrors.at(30u), second);
628 EXPECT_EQ(
first, 30u);
631TEST(MultiGpuMirroredActors, a_stale_mirror_of_another_kind_is_not_reused) {
632 FakeSecondary secondary;
633 secondary.counter = 50u;
634 secondary.actors[50u] = {
"vehicle.lincoln.mkz", {},
false,
false};
635 secondary.mirrors[30u] = 50u;
637 const auto plan = secondary.Plan(30u,
"sensor.camera.rgb");
638 EXPECT_FALSE(plan.reuse.has_value());
639 EXPECT_EQ(plan.spawn_id, 30u);
642TEST(MultiGpuSensorStreams, restarted_secondary_binds_overlapping_ids_to_their_own_streams) {
643 SecondaryStreams streams;
644 FakeSecondary secondary;
645 secondary.counter = 146u;
646 for (
int i = 0; i < 10; ++i) {
647 secondary.AddMapActor(
"traffic.stop");
649 std::map<uint32_t, carla::streaming::Stream> sensors;
650 for (uint32_t primary_id = 153u; primary_id <= 160u; ++primary_id) {
651 const uint32_t local_id = secondary.Replay(primary_id,
"sensor.camera.rgb");
652 auto it = sensors.find(local_id);
653 if (it == sensors.end()) {
654 it = sensors.emplace(local_id, streams.server.MakeStream()).first;
656 EXPECT_FALSE(streams.registry.Bind(streams.server, primary_id, StreamIdOf(it->second),
true).has_value());
658 ASSERT_EQ(sensors.size(), 8u);
660 for (uint32_t primary_id = 153u; primary_id <= 160u; ++primary_id) {
661 ASSERT_TRUE(streams.registry.Resolve(streams.server, primary_id, 1000u + primary_id).has_value());
663 streams.server.FindStreamAlias(1000u + primary_id),
664 std::optional<stream_id_type>(StreamIdOf(sensors.at(secondary.mirrors.at(primary_id)))));
668TEST(MultiGpuSensorStreams, self_aliased_broadcast_stream_is_reachable_with_aliases_on) {
671 SecondaryStreams secondary;
672 auto broadcast = secondary.server.MakeStream();
673 const stream_id_type broadcast_id = StreamIdOf(broadcast);
674 secondary.server.SetStreamAlias(broadcast_id, broadcast_id);
676 std::atomic_bool received{
false};
679 const auto token = secondary.server.FindToken(broadcast_id);
680 ASSERT_TRUE(token.has_value());
681 client.
Subscribe(*token, [&received](
auto) { received =
true; });
682 EXPECT_TRUE(WriteUntilReceived(broadcast, received));
686TEST(MultiGpuSensorStreams, episode_change_keeps_the_broadcast_alias) {
687 SecondaryStreams secondary;
688 auto broadcast = secondary.server.MakeStream();
689 const stream_id_type broadcast_id = StreamIdOf(broadcast);
690 secondary.server.SetStreamAlias(broadcast_id, broadcast_id);
691 auto sensor = secondary.server.MakeStream();
692 EXPECT_FALSE(secondary.registry.Bind(secondary.server, 7u, StreamIdOf(sensor),
true).has_value());
693 ASSERT_TRUE(secondary.registry.Resolve(secondary.server, 7u, 100u).has_value());
695 secondary.registry.BeginEpisodeChange(secondary.server);
696 EXPECT_FALSE(secondary.server.FindStreamAlias(100u).has_value());
697 EXPECT_EQ(secondary.server.FindStreamAlias(broadcast_id), std::optional<stream_id_type>(broadcast_id));
static std::shared_ptr< BufferView > CreateFrom(Buffer &&buffer)
Secondary-side map from a primary sensor actor to its local stream, exposed to clients only through a...
A client able to subscribe to multiple streams.
void AsyncRun(size_t worker_threads)
void Subscribe(const Token &token, Functor &&callback)
void UnSubscribe(const Token &token)
std::optional< stream_id > FindStreamAlias(stream_id alias)
void RemoveStreamAlias(stream_id alias)
bool IsEnabledForROS(stream_id sensor_id)
std::optional< token_type > FindToken(stream_id id)
Token of an existing stream, or std::nullopt if id is unknown.
void SetStreamAlias(stream_id alias, stream_id target)
void AsyncRun(size_t worker_threads)
void SetSessionsUseStreamAliases(bool enabled)
Token token() const
Token associated with this stream.
void Write(Buffers &&... buffers)
Flush buffers down the stream. No copies are made.
Serializes a stream endpoint.
const auto & get_stream_id() const
ReplayedActorPlan PlanReplayedActor(const MirroredActorMap &mirrors, uint32_t primary_id, const LocalActors &local)
Where a secondary replays the add event of primary actor primary_id.
carla::streaming::detail::token_type token_type
std::unordered_map< uint32_t, uint32_t > MirroredActorMap
Primary actor id -> id of the local actor a secondary replayed it onto, for the current episode only.
carla::SharedPtr< cc::Actor > Actor
constexpr uint16_t TESTING_PORT
TEST(streaming, find_token_does_not_create_a_stream_for_an_unknown_id)