[Sensor] SensorSuite in each SceneNode#1118
[Sensor] SensorSuite in each SceneNode#1118vauduong merged 49 commits intofacebookresearch:masterfrom
Conversation
| for (const SensorSpec::ptr& spec : sensorSetup) { | ||
| scene::SceneNode& sensorNode = node.createChild(); | ||
| // VisualSensor Setup | ||
| if (spec->isVisualSensorSpec()) { | ||
| if (spec->sensorSubType == SensorSubType::Orthographic || | ||
| spec->sensorSubType == SensorSubType::Pinhole) { | ||
| sensorSuite.add(CameraSensor::create( | ||
| sensorNode, std::dynamic_pointer_cast<CameraSensorSpec>(spec))); | ||
| sensor::CameraSensor::create( | ||
| sensorNode, | ||
| std::dynamic_pointer_cast<sensor::CameraSensorSpec>(spec)); | ||
| } |
There was a problem hiding this comment.
I'm having some trouble with initializing sensors here. Currently, the sensor is initialized and a reference is added to all the relevant node and subtree sensorSuites, as expected.
However, as soon as it is done initializing and the for loop moves onto the next SensorSpec, the sensor is destructed immediately.
I'm not sure why this is happening because the reference to the sensor should still exist as it was added to a map, right? Why is this happening and how can I get the Sensor to persist?
There was a problem hiding this comment.
the for loop moves onto the next SensorSpec
It means the local smart pointer will be deleted.
I'm not sure why this is happening because the reference to the sensor should still exist as it was added to a map, right? Why is this happening and how can I get the Sensor to persist?
No it is not right. When the smart pointer (a local variable) gets destroyed, the dtor of Sensor class is called, which means the sensor you just added to the map gets removed (you just coded it up by yourself in this PR).
2219949 to
20e2c78
Compare
|
@mosra @bigbike @Skylion007 @erikwijmans @aclegg3 @eundersander @jturner65 Thank you everyone for the feedback! I've addressed all the feedback so far, and am looking forwards to a final pass on this PR. There are some comments about the python implementation that have been left for reference, but they will be addressed in a separate PR. |
bigbike
left a comment
There was a problem hiding this comment.
I did not even go through all the PR. Too many problems need to be addressed already.
Stop reviewing now.
| .function("setLocalTransform", &Sensor_setLocalTransform) | ||
| .function("specification", &Sensor::specification); | ||
|
|
||
| em::class_<SensorSuite>("SensorSuite") |
There was a problem hiding this comment.
You only fixed the bindings, not the actual web utility who would call such bindings. I am worried if this will cause failure in the web version. Can you figure out the web utility and double confirm this is not the case?
There was a problem hiding this comment.
Took a deeper look and none of the demos except the vr demo will call getSensorSuite, but there are some issues that I posted about in slack and am still investigating.
emscripten can't use reference types to interact with js, only pointers and primitives https://stackoverflow.com/questions/55905148/emscripten-pass-stl-c-map-parameter
It seems there's a workaround to write a policy to get pointers from references, but I'm not sure if this is what we want to do here. emscripten-core/emscripten#3480
Can anyone provide some guidance?
There was a problem hiding this comment.
How about have JS getSensors return pointers instead of references? And then instead of binding it directly to SensorSuite::getSensors, bind it to some new C++ helper function that constructs the map of pointers.
There was a problem hiding this comment.
Thank you for the suggestion eundersander! vr demo is fixed and working
| std::map<std::string, std::reference_wrapper<sensor::Sensor>>::iterator it = | ||
| nodeSensorSuite_->getSensors().begin(); | ||
| if (it != nodeSensorSuite_->getSensors().end()) { | ||
| CORRADE_ASSERT(static_cast<scene::SceneNode*>(this->parent()) != nullptr, |
There was a problem hiding this comment.
Clearly you do not understand what is the difference between the static_cast and the dynamic_cast.
If the item is not convertible, static_cast will NOT return nullptr. So basically what you wrote here will not work.
I remembered I clearly told you to use dynamic_cast during our 1:1.
| std::map<std::string, std::reference_wrapper<sensor::Sensor>>::iterator it = | ||
| nodeSensorSuite_->getSensors().begin(); | ||
| if (it != nodeSensorSuite_->getSensors().end()) { | ||
| CORRADE_ASSERT(static_cast<scene::SceneNode*>(this->parent()) != nullptr, |
There was a problem hiding this comment.
Here, the same thing. static_cast is the problem.
And you just simply copy and paste whatever you have in the addSensorToParentNodeSensorSuite.
But clearly the logic is different in an "add" and a "remove" functions.
Here, if you find you cannot dynamic_cast to its parent, one case is that the parent (SceneNode) is already deconstructed. So the conversion fails., which means in this case, you do not need to do anything. (The logic you have here is to let the program exit due to ASSERTION failure. It is totally wrong.)
There was a problem hiding this comment.
Ok, thanks for the clarification!
|
Hi, I've been repeatedly pinged in this thread in review comments. I believe you're meaning to ping someone else. Just letting you know. |
|
@vivian: Sorry! I sincerely apologize! It was my mistake. I am pretty sure this would be the last time to ping you in this PR. |
| @@ -12,26 +14,173 @@ namespace scene { | |||
|
|
|||
| SceneNode::SceneNode(SceneNode& parent) | |||
There was a problem hiding this comment.
Hold on here:
You have code duplication in the constructors.
You need constructor delegation.
| } | ||
|
|
||
| SceneNode& SceneNode::createChild(SceneNodeTags childNodeTags) { | ||
| CORRADE_ASSERT( |
There was a problem hiding this comment.
Are these functions (createChild, setParent etc.) exposed in the python side?
If yes, let use ESP_CHECK in the esp/core/Check.h in these functions as it is more friendly for the py users.
There was a problem hiding this comment.
If you already have ESP_CHECK, you do not need the CORRADE_ASSERT any more. You should read the comments about the ESP_CHECK before you use it.
| // Perform same internal checks as magnum to ensure newParent is a valid new | ||
| // parent before updating any SensorSuites | ||
| // Parent can not be leaf node | ||
| CORRADE_ASSERT(!(newParent->getSceneNodeTags() & SceneNodeTag::Leaf), |
There was a problem hiding this comment.
Again, CORRADE_ASSERT is not needed any more.
| void SceneNode::removeSubtreeSensorsFromAncestors() { | ||
| for (const auto& sensor : subtreeSensorSuite_->getSensors()) { | ||
| SceneNode* currentNode = dynamic_cast<SceneNode*>(this->parent()); | ||
| while (currentNode && !SceneGraph::isRootNode(*currentNode)) { |
There was a problem hiding this comment.
if currentNode is the root node, then this piece of code will not remove the sensors from the currentNode.
There was a problem hiding this comment.
Go back to the slack channel, read the code I gave you very carefully, and try to understand the difference between my code and the code you wrote here.
There was a problem hiding this comment.
In your tests, DO check if the number of sensors is correct in the root node!!
There was a problem hiding this comment.
Thank you, this is really important. Here is the code from slack:
SceneNode* currentNode = this;
do {
currentNode = dynamic_cast<SceneNode*>(currentNode->parent());
// no need to worry currentNode could be nullptr, no chance
currentNode->getSubtreeSensorSuite().getSensors().erase(sensor.first);
} while (!SceneGraph::isRootNode(*currentNode));
We had discussed previously that currentNode could never be nullptr, which made sense because the currentNode would never be nullptr before reaching rootNode's parent, which it would not reach due to the while condition. However, this currentNode was actually coming back nullptr sometimes, and this was causing the segfault when running examples.py. My update to check that currentNode was not nullptr fixed the segfault, but overlooked adding/removing sensors from the root node's SensorSuites.
I have updated the logic to check that currentNode is not nullptr and to also update rootNode. I have also added checks for rootNode's SensorSuites in SensorTest.
Thanks and let me know if you have any questions.
| void SceneNode::addSubtreeSensorsToAncestors() { | ||
| for (const auto& sensor : subtreeSensorSuite_->getSensors()) { | ||
| SceneNode* currentNode = dynamic_cast<SceneNode*>(this->parent()); | ||
| while (currentNode && !SceneGraph::isRootNode(*currentNode)) { |
There was a problem hiding this comment.
So it means the root node will never ever update its sensorSuite.
| * this Agent's SceneNode and its children values. | ||
| * NOTE: This is only called in JS, as emscripten needs pointers | ||
| */ | ||
| std::map<std::string, sensor::Sensor::ptr> jsGetSubtreeSensors() { |
There was a problem hiding this comment.
Let's avoid enlarging this class's public interface unnecessarily. I had in mind a global static function defined in bindings_js.cpp and not exposed to other parts of the codebase. We don't want to litter the C++ codebase with js-specific code.
| .function("hasAction", &Agent::hasAction) | ||
| .function("act", &Agent::act) | ||
| .function("getSubtreeSensors", &Agent::getSubtreeSensors); | ||
| .function("getSubtreeSensors", &Agent::jsGetSubtreeSensors); |
There was a problem hiding this comment.
You can bind to a free function (a static global function defined in this file) instead of a Agent member method.
There was a problem hiding this comment.
Thanks for the clarification Eric! That's been addressed and is tested and working.
| SceneNode::SceneNode() : Mn::SceneGraph::AbstractFeature3D{*this} { | ||
| setCachedTransformations(Mn::SceneGraph::CachedTransformation::Absolute); | ||
| absoluteTransformation_ = absoluteTransformation(); | ||
| // nodeSensorSuite_ and subtreeSensorSuite_ will be released upon SceneNode's |
There was a problem hiding this comment.
// Once created, nodeSensorSuite_ and subtreeSensorSuite_ are features owned by the scene graph node. No need to release them in the destructor since magnum scene graph will handle it.
There was a problem hiding this comment.
Thank you! Let me know if there are any other changes you think I should make.
| } | ||
|
|
||
| SceneNode& SceneNode::createChild(SceneNodeTags childNodeTags) { | ||
| CORRADE_ASSERT( |
There was a problem hiding this comment.
If you already have ESP_CHECK, you do not need the CORRADE_ASSERT any more. You should read the comments about the ESP_CHECK before you use it.
| // Perform same internal checks as magnum to ensure newParent is a valid new | ||
| // parent before updating any SensorSuites | ||
| // Parent can not be leaf node | ||
| CORRADE_ASSERT(!(newParent->getSceneNodeTags() & SceneNodeTag::Leaf), |
There was a problem hiding this comment.
Again, CORRADE_ASSERT is not needed any more.
| static sensor::SensorSuite createSensors( | ||
| scene::SceneNode& node, | ||
| const sensor::SensorSetup& sensorSetup); | ||
| static void deleteSensor(scene::SceneNode& node, const std::string& uuid); |
There was a problem hiding this comment.
Just read the comments. So consider call it deleteSubtreeSensor to avoid confusion.
Motivation and Context
Due to new functionality from #1064, there needs to be more functionality to keep track of sensors.
In this PR:
How Has This Been Tested
Build and test, write additional unit tests
Types of changes
Checklist