Skip to content

Commit

Permalink
Add a locking mechanism to the Registry for thread safe operation in …
Browse files Browse the repository at this point in the history
…conjuction with background services.
  • Loading branch information
gwaldron committed Nov 18, 2024
1 parent 1a4d87c commit 48433eb
Show file tree
Hide file tree
Showing 21 changed files with 433 additions and 383 deletions.
35 changes: 20 additions & 15 deletions src/apps/rocky_demo/Demo_Decluttering.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,21 @@ namespace
class DeclutterSystem
{
public:
DeclutterSystem(entt::registry& in_registry)
: registry(in_registry) { }
DeclutterSystem(ecs::Registry& in_registry)
: _registry(in_registry) { }

entt::registry& registry;
float update_hertz = 1.0f; // updates per second
bool enabled = true;
double buffer_radius = 25.0;
bool sort_by_priority = true;

unsigned total = 0, visible = 1;
std::size_t last_max_size = 32;
unsigned visible = 1;
unsigned total = 0;
std::function<std::vector<std::uint32_t>()> getActiveViewIDs;

static std::shared_ptr<DeclutterSystem> create(entt::registry& registry) {
ecs::Registry& _registry;
std::size_t _last_max_size = 32;

static std::shared_ptr<DeclutterSystem> create(ecs::Registry& registry) {
return std::make_shared<DeclutterSystem>(registry);
}

Expand All @@ -55,20 +56,21 @@ namespace
// First collect all declutter-able entities and sort them by their distance to the camera.
// tuple = [entity, x, y, sort_key, width, height]
std::vector<std::tuple<entt::entity, double, double, double, int, int>> sorted;
sorted.reserve(last_max_size);
sorted.reserve(_last_max_size);

auto [lock, registry] = _registry.read();

double aspect_ratio = 1.0; // same for all objects
auto view = registry.view<Declutter, Transform, Visibility>();
for (auto&& [entity, declutter, transform, visibility] : view.each())
{
if (visibility.active && transform.node && transform.node->viewLocal.size() > viewID)
{
int width =
int width =
(declutter.width_px >= 0 ? declutter.width_px : 0) +
(declutter.buffer_x >= 0 ? declutter.buffer_x : (int)buffer_radius);
int height =
int height =
(declutter.height_px >= 0 ? declutter.height_px : 0) +
(declutter.buffer_y >= 0 ? declutter.buffer_y : (int)buffer_radius);
(declutter.buffer_y >= 0 ? declutter.buffer_y : (int)buffer_radius);

// Cheat by directly accessing view 0. In reality we will might either declutter per-view
// or have a "driving view" that controls visibility for all views.
Expand All @@ -86,7 +88,7 @@ namespace

// sort them by whatever sort key we used, either priority or camera distance
std::sort(sorted.begin(), sorted.end(), [](const auto& a, const auto& b) { return std::get<3>(a) > std::get<3>(b); });
last_max_size = sorted.size();
_last_max_size = sorted.size();

// Next, take the sorted vector and declutter by populating an R-Tree with rectangles representing
// each entity's buffered location in screen(clip) space. For objects that don't conflict with
Expand All @@ -105,8 +107,8 @@ namespace
double half_width = (double)(width >> 1);
double half_height = (double)(height >> 1);

double LL[2]{ x - half_width, y - half_height * aspect_ratio };
double UR[2]{ x + half_width, y + half_height * aspect_ratio };
double LL[2]{ x - half_width, y - half_height };
double UR[2]{ x + half_width, y + half_height };

if (rtree.Search(LL, UR, [](auto e) { return false; }) == 0)
{
Expand All @@ -125,6 +127,8 @@ namespace

void resetVisibility()
{
auto [lock, registry] = _registry.read();

auto view = registry.view<Declutter, Visibility>();
for (auto&& [entity, declutter, visibility] : view.each())
{
Expand Down Expand Up @@ -153,6 +157,7 @@ auto Demo_Decluttering = [](Application& app)
while (!token.canceled())
{
run_at_frequency f(declutter->update_hertz);

if (declutter->enabled)
{
declutter->update(app.runtime());
Expand Down
213 changes: 111 additions & 102 deletions src/apps/rocky_demo/Demo_Geocoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ auto Demo_Geocoder = [](Application& app)

if (entity == entt::null)
{
auto [lock, registry] = app.registry.write();

// Load an icon image
auto& io = app.io();
auto image = io.services.readImageFromURI("https://readymap.org/readymap/filemanager/download/public/icons/placemark32.png", io);
Expand All @@ -42,10 +44,10 @@ auto Demo_Geocoder = [](Application& app)
}

// Make an entity to host our icon:
entity = app.registry.create();
entity = registry.create();

// Attach the new Icon and set up its properties:
auto& icon = app.registry.emplace<Icon>(entity);
auto& icon = registry.emplace<Icon>(entity);
icon.image = image.value;
icon.style = IconStyle{ 32, 0.0f }; // pixel size, rotation(radians)

Expand All @@ -61,132 +63,139 @@ auto Demo_Geocoder = [](Application& app)
label_style_area.pointSize = 26.0f;
label_style_area.outlineSize = 0.5f;

auto& label = app.registry.emplace<Label>(entity);
auto& label = registry.emplace<Label>(entity);

// Outline for location boundary:
feature_view.styles.line = LineStyle();
feature_view.styles.line->color = vsg::vec4{ 1, 1, 0, 1 };
feature_view.styles.line->depth_offset = 9000.0f; //meters

app.registry.get<Visibility>(entity).active = false;
//ecs::setVisible(app.registry, feature_view.entity, false);
registry.get<Visibility>(entity).active = false;

// Transform to place the entity:
auto& xform = app.registry.emplace<Transform>(entity);
auto& xform = registry.emplace<Transform>(entity);
}

if (ImGuiLTable::Begin("geocoding"))
else
{
if (ImGuiLTable::InputText("Location:", input_buf, 256, ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll))
auto [lock, registry] = app.registry.read();

if (ImGuiLTable::Begin("geocoding"))
{
// hide the placemark:
auto& icon = app.registry.get<Icon>(entity);
app.registry.get<Visibility>(entity).active = false;

std::string input(input_buf);
geocoding_task = jobs::dispatch([&app, input](jobs::cancelable& c)
{
Result<std::vector<Feature>> result;
if (!c.canceled())
if (ImGuiLTable::InputText("Location:", input_buf, 256, ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll))
{
// hide the placemark:
auto& icon = registry.get<Icon>(entity);
registry.get<Visibility>(entity).active = false;

std::string input(input_buf);
geocoding_task = jobs::dispatch([&app, input](jobs::cancelable& c)
{
Geocoder geocoder;
result = geocoder.geocode(input, app.io());
}
return result;
});
Result<std::vector<Feature>> result;
if (!c.canceled())
{
Geocoder geocoder;
result = geocoder.geocode(input, app.io());
}
return result;
});
}
ImGuiLTable::End();
}
ImGuiLTable::End();
}

if (geocoding_task.working())
{
ImGui::Text("Searching...");
}
if (geocoding_task.working())
{
ImGui::Text("Searching...");
}

else if (geocoding_task.available())
{
auto& result = geocoding_task.value();
if (result.status.ok())
else if (geocoding_task.available())
{
ImGui::Text("Click on a result to center:");
for (auto& feature : result.value)
auto& result = geocoding_task.value();
if (result.status.ok())
{
bool selected = false;
ImGui::Separator();
std::string display_name = feature.field("display_name").stringValue;
ImGui::Selectable(display_name.c_str(), &selected);
if (selected)
ImGui::Text("Click on a result to center:");
for (auto& feature : result.value)
{
app.onNextUpdate([&, display_name]()
{
auto extent = feature.extent;
if (extent.area() == 0.0)
extent.expand(Distance(10, Units::KILOMETERS), Distance(10, Units::KILOMETERS));

auto view = app.displayManager->windowsAndViews.begin()->second.front();
auto manip = MapManipulator::get(view);
if (manip)
bool selected = false;
ImGui::Separator();
std::string display_name = feature.field("display_name").stringValue;
ImGui::Selectable(display_name.c_str(), &selected);
if (selected)
{
app.onNextUpdate([&app, &feature, display_name]()
{
Viewpoint vp = manip->getViewpoint();
vp.point = extent.centroid();
manip->setViewpoint(vp, std::chrono::seconds(2));
}

auto&& [icon, label] = app.registry.get<Icon, Label>(entity);
auto extent = feature.extent;
if (extent.area() == 0.0)
extent.expand(Distance(10, Units::KILOMETERS), Distance(10, Units::KILOMETERS));

auto view = app.displayManager->windowsAndViews.begin()->second.front();
auto manip = MapManipulator::get(view);
if (manip)
{
Viewpoint vp = manip->getViewpoint();
vp.point = extent.centroid();
manip->setViewpoint(vp, std::chrono::seconds(2));
}

auto [lock, registry] = app.registry.read();

auto&& [icon, label] = registry.get<Icon, Label>(entity);

// show the placemark:
if (feature.geometry.type == Geometry::Type::Points)
{
registry.get<Visibility>(entity).active = true;
if (feature_view.entity != entt::null)
registry.get<Visibility>(feature_view.entity).active = false;

label.style = label_style_point;
}
else
{
// update the mesh:
auto copy_of_feature = feature;
copy_of_feature.geometry.convertToType(Geometry::Type::LineString);
Geometry::iterator i(copy_of_feature.geometry);
while (i.hasMore()) for (auto& point : i.next().points) point.z = 500.0;
feature_view.clear(registry);
feature_view.features = { copy_of_feature };
feature_view.generate(registry, app.mapNode->worldSRS(), app.runtime());

registry.get<Visibility>(entity).active = true;
if (feature_view.entity != entt::null)
registry.get<Visibility>(feature_view.entity).active = true;

label.style = label_style_area;
}

// update the label:
auto text = display_name;
replace_in_place(text, ", ", "\n");
label.text = text;
label.revision++;

// position it:
auto& xform = registry.get<Transform>(entity);
xform.setPosition(extent.centroid());
});
}
}
ImGui::Separator();
if (ImGui::Button("Clear"))
{
geocoding_task.reset();
input_buf[0] = (char)0;

// show the placemark:
if (feature.geometry.type == Geometry::Type::Points)
{
app.registry.get<Visibility>(entity).active = true;
if (feature_view.entity != entt::null)
app.registry.get<Visibility>(feature_view.entity).active = false;
label.style = label_style_point;
}
else
{
// update the mesh:
auto copy_of_feature = feature;
copy_of_feature.geometry.convertToType(Geometry::Type::LineString);
Geometry::iterator i(copy_of_feature.geometry);
while(i.hasMore()) for (auto& point : i.next().points) point.z = 500.0;
feature_view.clear(app.registry);
feature_view.features = { copy_of_feature };
feature_view.generate(app.registry, app.mapNode->worldSRS(), app.runtime());
app.registry.get<Visibility>(entity).active = true;
if (feature_view.entity != entt::null)
app.registry.get<Visibility>(feature_view.entity).active = true;
label.style = label_style_area;
}

// update the label:
auto text = display_name;
replace_in_place(text, ", ", "\n");
label.text = text;
label.revision++;

// position it:
auto& xform = app.registry.get<Transform>(entity);
xform.setPosition(extent.centroid());
});
registry.get<Visibility>(entity).active = false;
if (feature_view.entity != entt::null)
registry.get<Visibility>(feature_view.entity).active = false;
}
}
ImGui::Separator();
if (ImGui::Button("Clear"))
else
{
geocoding_task.reset();
input_buf[0] = (char)0;

app.onNextUpdate([&]()
{
app.registry.get<Visibility>(entity).active = false;
if (feature_view.entity != entt::null)
app.registry.get<Visibility>(feature_view.entity).active = false;
});
ImGui::TextColored(ImVec4(1, 0.5, 0.5, 1), std::string("Geocoding failed! " + result.status.message).c_str());
}
}
else
{
ImGui::TextColored(ImVec4(1, 0.5, 0.5, 1), std::string("Geocoding failed! " + result.status.message).c_str());
}
}
};
16 changes: 10 additions & 6 deletions src/apps/rocky_demo/Demo_Icon.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ auto Demo_Icon = [](Application& app)

if (entity == entt::null)
{
auto [lock, registry] = app.registry.write();

// Load an icon image
auto io = app.instance.io();
auto image = io.services.readImageFromURI("https://readymap.org/readymap/filemanager/download/public/icons/BENDER.png", io);
Expand All @@ -35,26 +37,28 @@ auto Demo_Icon = [](Application& app)
}

// Make an entity to host our icon:
entity = app.registry.create();
entity = registry.create();

// Attach the new Icon and set up its properties:
auto& icon = app.registry.emplace<Icon>(entity);
auto& icon = registry.emplace<Icon>(entity);
icon.image = image.value;
icon.style = IconStyle{ 75, 0.0f }; // pixel size, rotation(radians)

// Transform to place the icon:
auto& transform = app.registry.emplace<Transform>(entity);
auto& transform = registry.emplace<Transform>(entity);
transform.setPosition(GeoPoint(SRS::WGS84, 0, 0, 50000));
transform.localTangentPlane = false; // optimization for billboards :)
}

if (ImGuiLTable::Begin("icon"))
{
bool visible = ecs::visible(app.registry, entity);
auto [lock, registry] = app.registry.read();

bool visible = ecs::visible(registry, entity);
if (ImGuiLTable::Checkbox("Show", &visible))
ecs::setVisible(app.registry, entity, visible);
ecs::setVisible(registry, entity, visible);

auto& icon = app.registry.get<Icon>(entity);
auto& icon = registry.get<Icon>(entity);

if (ImGuiLTable::SliderFloat("Pixel size", &icon.style.size_pixels, 1.0f, 1024.0f))
icon.revision++;
Expand Down
Loading

0 comments on commit 48433eb

Please sign in to comment.