Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#291 Fixing Pickshapes using farseer #579

Merged
merged 9 commits into from
Oct 18, 2017
Merged

#291 Fixing Pickshapes using farseer #579

merged 9 commits into from
Oct 18, 2017

Conversation

Barsonax
Copy link
Member

No description provided.

@ilexp
Copy link
Member

ilexp commented Oct 13, 2017

Since there's a ToDo comment, is this still WiP? Let me know when it's ready for review 👍

@Barsonax
Copy link
Member Author

Barsonax commented Oct 13, 2017

The chainshape part is buggy so I left that out for now. Just did the circle and poly shapes for now. The chainshapes were not working before either so its not breaking anything.

@ilexp
Copy link
Member

ilexp commented Oct 13, 2017

I'll take a look as soon as I get around to it. Since polygons and circles only would be equal to the old functionality, we could merge this PR even without the chain shape checks after review. Should be fine as long as we don't introduce a regression.

Copy link
Member

@ilexp ilexp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perf-wise, maybe provide a private (!) version of the fully parameterized instance PickShapes method that takes a Farseer polygon shape instance and (by-ref) transform? You could call this method from the regular public overload, and the static PickShapesGlobal method: No added benefit for the first one, but the second one would only create and configure the shape once.

Final question: How's performance compared to before? (And which test setup did you use? Good to know for context.)

So far I like the Farseer version of this fix much better, very compact and self-contained. Nice work on this 👍

{
pickedShapes.Add(s);
continue;
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's a polygon shape, but does not have any collisions, it will continue checking whether it could be a circle shape next. Consider changing this to an if-else structure, or moving the continue statement out of the point count check!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ye missed that, it should obviously not do the circle check after it already done the polygon one.

Too bad we dont have C# 7 as pattern matching would make this so much more readable.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found out there is a ShapeType enum property on the fixture so I can use that in a switch. Much more readable.

Vector2 fsWorldCoord = PhysicsUnit.LengthToPhysical * worldCoord;
FarseerPhysics.Collision.AABB fsWorldAABB = new FarseerPhysics.Collision.AABB(fsWorldCoord, PhysicsUnit.LengthToPhysical * (worldCoord + size));

PolygonShape boxShape = new PolygonShape(new Vertices(new List<Vector2>() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Glad you didn't bother keeping the old fsWorldCoord naming / variable scheme. That was terrible. This one looks definitely cleaner.

PhysicsUnit.LengthToPhysical * new Vector2(worldCoord.X + size.X, worldCoord.Y),
PhysicsUnit.LengthToPhysical * (worldCoord + size),
PhysicsUnit.LengthToPhysical * new Vector2(worldCoord.X, worldCoord.Y + size.Y) }), 1);
Mat22 rot = new Mat22();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a newline, so the box transform creation is its own block, similar to the body transform retrieval below. Also, maybe move the oldCount and manifold declarations down to directly in front of the loop, so it doesn't get mixes up with the transform creation blocks?

Mat22 rot = new Mat22();
rot.SetIdentity();
Vector2 pos = new Vector2(0, 0);
FarseerPhysics.Common.Transform boxTransform = new FarseerPhysics.Common.Transform(ref pos, ref rot);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are transform position and rotation public fields? If so, no need to declare and assign rotation and position separately! Just call SetIdentity directly on the box transform rotation field, and assign the position directly without declaring an extra variable. Could also use the default / empty constructor in that case.

(If they are not public fields, disregard my comment.)

{
pickedShapes.Add(s);
break;
continue;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above!

// {
// var jNext = j + 1;
// if (jNext >= chainShape.Vertices.Count) jNext = 0;
// var edgeShape = new EdgeShape(chainShape.Vertices[j], chainShape.Vertices[jNext]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if that will fix it, but instead of creating a new edge shape every loop iteration, create it once and let the chain shape configure it. There's a GetChildEdge method that is public and I believe also used by Farseers internals, here.

Alternatively, if you want to keep chain shapes our of this PR for now, maybe just add a note to the commented out section regarding this, for future reference.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was still experimenting with this part. I already tried it with GetChildEdge but got some weird results so have to investigate that some more. Not for this PR though so Ill put a clarifying comment there

@Barsonax
Copy link
Member Author

When I get back home I will give you the setup I used for measuring performance. So far the performance was slower than my own implementation.

Good point about the private parameterized instance method I will add that.

@Barsonax
Copy link
Member Author

Barsonax commented Oct 14, 2017

Tested the performance and compared to my own implementation the farseer is slower. How much depends on the shape and where the collision is:
-Circles are ~2x as slow. The performance is pretty much constant no matter where you touch the circle
-Simple polygons (3 vertices). are 1.2x-3x as slow. The farseer version seems to have a constant performance no matter where you touch the polygon while in my own implementation it matters what edge or vertice you touch.
-Complex polygons (14 vertices). The farseer version seems to again have a constant performance no matter where you touch the polygon. The performance of my own implementation seems to vary as much as 10x depending on where you touch the polygon. Roughly speaking they have the same performance but farseer is more balanced. There seems to be a trend that the more complex polygons seem to favor farseer.

Now the question is is it worth sacrificing some performance to avoid duplicate collision code? Note that my own implementation is not optimized yet. I think it should be possible to operate directly on the farseer data now I understand farseer a little better for instance.

Tbh I don't think anyone will be running in performance issues anytime soon with PickShapes as I had to call it quite a lot of times before you could start noticing it.

Attached is the Data and Source folder I used when testing this. To use:

  1. Copy the Data and Source folder to the duality/Build/Output
  2. Build the project in the branch you want to test (this one or the other one with my implementation)
  3. Debug the ProjectPlugins solution
  4. The time it took to perform the PickShapesGlobal is printed to the output. Note that PickShapesGlobal is called 50000 times per update in order to make the performance more visible.

PickshapesTest.zip

Note that there are currently no unit tests in this branch. I could copy some over from #577.

FarseerPhysics.Common.Transform boxTransform = new FarseerPhysics.Common.Transform();
boxTransform.SetIdentity();

return PickShapes(boxShape, boxTransform, pickedShapes);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use this. for instance member access as well. Would merge anyway since it's really just a detail, so whoever of us gets to it first can do it.

@ilexp
Copy link
Member

ilexp commented Oct 15, 2017

Changes look good so far!

Now the question is is it worth sacrificing some performance to avoid duplicate collision code?

Good question! In this case, I think the answer should be "yes, if it's not too severe", but it should definitely be on a list of potential optimization for when issue #570 is done.

Will take a closer look at performance myself before merging - if it doesn't have a serious impact except in benchmarks, I think it should be fine. Will have to see how close it is to real-world impact.

ilexp added 2 commits October 15, 2017 13:16
#FIX: Fixed a bug where complex polygon shapes would be returned multiple times in the result list of PickShapes when intersecting more than one convex segment.
#CHANGE: Renamed local variable from single-letter indexer style to a descriptive name.
@ilexp
Copy link
Member

ilexp commented Oct 15, 2017

Note that there are currently no unit tests in this branch. I could copy some over from #577.

Took a brief look and probably wouldn't do a merge for them as-is, so I'd keep this PR separate for now so we can get it merged. Tests for this would be great, but I'd probably do a draft / design pass in the discussion of #533 first before implementing it.


Found a small bug where a ShapeInfo was sometimes added multiple times to the result list for complex polygons and fixed it.

Looked into performance and getting between 30ms and 350ms for 50000 tests / up to 0.007 ms per test on my machine depending on which bodies are checked - not great, but not catastrophic either. I do see some optimization potential on the Farseer side of things, but that's an issue for another day.

A bit busy right now, so will have to do it later, but I'd say this is a merge. Nice work 👍

ToDo:

@Barsonax
Copy link
Member Author

Hmm well the contains check also adds overhead in all cases atm. Not sure what would be faster since that will also depend on how much shapes you are picking. This would need benchmarking.

Changing the signature would break compatibility so definitely not for v2.

@ilexp
Copy link
Member

ilexp commented Oct 15, 2017

Hmm well the contains check also adds overhead in all cases atm. Not sure what would be faster since that will also depend on how much shapes you are picking. This would need benchmarking.

Yep, but also different kinds of overhead! Perf right now, but design in the other case, e.g. forcing the callsite to use a more complex collection. Totally agree on the perf consideration though, if it shows up on the profiler at some point, this will have to change.

@ilexp ilexp merged commit c8cecf1 into AdamsLair:master Oct 18, 2017
@ilexp ilexp added this to the General milestone Oct 18, 2017
@ilexp ilexp added Bug It's broken and should be fixed Cleanup Improving form, keeping function Core Area: Duality runtime or launcher labels Oct 18, 2017
@Barsonax Barsonax deleted the #291_Farseer branch October 31, 2018 10:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug It's broken and should be fixed Cleanup Improving form, keeping function Core Area: Duality runtime or launcher
Development

Successfully merging this pull request may close these issues.

2 participants