Performance Issue: Increasing Time in "cTrav"?

In attempting to optimise a particular element of my current project, I bumped into an unexpected degradation of performance over time.

On investigating via PStats, it seems that this stems from the collision side of things: I see the time spent in App->Collisions->ctrav increase as the frame-rate decreases.

Oddly, the number of collision tests and collision volumes does not seem to increase. (Although the latter does seem like it might be a little high.)

What might be causing this?

I’m also slightly mystified to discover that, under “Collision Volumes”, PStats reports not only CollisionCapsules, CollisionPolygons, and CollisionNodes–but also PandaNodes. And quite a lot of them! Why might such be counted in this section…?

You’ve probably thought of it, but did you try first setting the collideMask off on your visible geometry?:

visibleNp.setCollideMask(BitMask32.allOff())

Then setting certain masks on for your actual collision geometry?:

intoMaskExampleA=BitMask32.bit(4)
fromMaskExampleA=BitMask32.bit(4)
collisionNpInto.setCollideMask(BitMask32.allOff())
collisionNpInto.node().setIntoCollideMask(intoMaskExampleA)
collisionNpFrom.setCollideMask(BitMask32.allOff())
collisionNpFrom.node().setFromCollideMask(fromMaskExampleA)

Doing that for all your colliders should ensure no PandaNodes are tested for collisions, if I’m not mistaken.

Generally speaking, collision with visible geometry is off by default, I believe. Thus it shouldn’t be called for to explicitly disable it, unless it has been enabled prior.

And indeed, I do have masks applied.

Nevertheless, thank you for the suggestions! :slight_smile:

True, but I noticed when dealing with fully procedural geometry at least, whenever I didn’t set it off with the aforementioned command, it would get tested and return results from a collisionHandlerQueue for example, just saying.

That’s fair–if unexpected. Still, I have very little procedural geometry right now, I believe.

Update: I believe that I’ve found the problem.

In short: It seems that I had a sub-class the “destroy” method of which didn’t call the parent-class’s “destroy” method. As a result, the node-cleanup being done in the latter method wasn’t being performed by the sub-class, resulting in a leak of nodes.

Not to bother you, but out of curiosity and since I’m facing a somewhat similar problem, were you able to find out what was causing PandaNodes to get tested as collision volumes? Are they still being reported or did your fix clean that up as well?

Not a both at all. :slight_smile:

Honestly, I still don’t know.

If I were to guess, I would suspect that there are PandaNodes attached to CollisionNodes somewhere–but that is very much a guess.

I do still see them there, indeed. o_0

It would be useful if you could get the collisionTraverser to print out the names of the volumes it’s about to test. Trying to use the recorder as such:

CollisionRecorder *recorder_pointer;
CollisionTraverser traverser;
traverser.set_recorder(recorder_pointer);
...
recorder_pointer->output(cout);
...
CollisionRecorder *rec_got=traverser.get_recorder();
rec_got->output(cout);

Yields a freeze, since the recorder is set to null for some reason, maybe I’m setting it up improperly. I’m not sure how to set up the recorder from a python perspective. Perhaps you have some experience in this regard? In any case, getting the names would be helpful, so I’ll keep trying to test it…

I’ll confess that I didn’t actually know about that feature!

Looking it up, it looks like “CollisionRecorder” is a virtual class–a blank base from which an actual class should be derived. It’s thus not usable in its un-derived form, I daresay.

I see that “CollisionVisualizer” inherits from it–perhaps you could use that class?

Well, taking your advice this way:

self.vizi=CollisionVisualizer("visualizer")
base.cTrav.setRecorder(self.vizi)

...
def taskFunc(task):
    self.vizi.output(panda3d.core.Notify.out())
    return task.cont

Doesn’t make the program freeze, yet provides rather shallow information, no nodes are named and it only prints the number of detected collision solids and the number of tests conducted, e.g.:

CollisionVisualizer visualizer tested 3, detected 3

However, it strangely prevents this issue from erupting:

Which to me is strange. Maybe because by invoking

panda3d.core.Notify.out()

It somehow prevents other errors from being printed out? But I shall not speculate, best to wait and see if someone more informed on this will respond with the right answer. For the time being, it seems to have stopped that error from crashing the application.
Terribly sorry for hijacking your thread this way :grimacing:, but I just thought I’d highlight this.

1 Like

Ah, that’s a pity. But perhaps someone more familiar with that class than we two might know better what to do with it…

I don’t think that preventing the printing of errors would likely prevent an AssertionError from stopping the program. It might crash silently, I suppose, but I would expect it to still crash.

More likely, I think, something else is happening to prevent the crash…

For my part, that’s okay–although I don’t know how rdb will respond!

That’s collision with visible geometry. It’s enabled if the nodes in question have their collide mask set such that they overlap with the “from” bits of your colliders. Note that all PandaNodes have some bits enabled for visible geometry collision by default, I believe the higher n bits, but you can check this yourself.

Ah, I see–I had forgotten, I think, that visible geometry actually has a default collision-mask!

It looks like that default collision-mask uses bit 21. While I don’t seem to be using that bit, specifically, I am using some fairly high bit-numbers, and I suppose that it’s possible that bit 21 has crept in somewhere unbeknownst…

Tests thus far haven’t found a collider with a mask that shares bits with the visible geometry mask, but I may well do more searching next week!

Thank you for the answer on this! :slight_smile:

Okay, I’m finding myself uncertain of how to proceed in discovering the offending PandaNode collision-sources.

The obvious checks have thus far failed: I haven’t found any non-CollisionNodes that are being assigned into-masks, nor any CollisionNodes that are being assigned anything that seems to overlap with the default mask used for visible geometry.

Is there some way that I might locate or print out the PandaNodes that collision system is operating on, or the CollisionNodes that it’s deeming to potentially collide with those PandaNodes…?

I think you can perform a search in the render tree and check all objects of the collision nodes type. Next, you may want to study them in detail.

I’m just not sure where the collision polygons that are generated during the execution of the program are stored, if a bit 21 was involved to collide with the visible geometry.

I did try a recursive walk through the scene-graph, starting at “render”, but didn’t find any colliders that seemed to have bit 21 set, alas. :/

They should be present in the scene-graph, just as any other node, I would think.

I would hope not, at least.

That said, I don’t think that I have that much procedurally-generated polygonal collision-geometry in my game at the moment–most of the colliders that I’m generating in code use built-in shapes, I believe.

Maybe try adding this: “notify-level-collide spam” to the config.prc file to get more information on collisions?

I can perhaps try it! (Tomorrow, as it’s late here. ^^; )

By the way, from your recent thread regarding a collision assertion error, I noted the following line in your code:

“All on” should, I would imagine, include the bit for visible geometry. This might be the reason that you’ve been seeing PandaNodes in your collision-reports: due to the inclusion of the “visible geometry” bit, non-collision nodes are being included in your collisions, and thus are being reported (as PandaNodes).

(I imagine that there’s something at least sort-of similar going in in my case–but I don’t know as-yet what might be causing it…)

All right, the output produced by “notify-level-collide spam” does look potentially informative–but I could use some aid in interpreting it, please.

Looking at what I have, it’s hard to say quite how much belongs to a single frame. However, I do see that it has sections marked by “begin_group” and “end_group”, so I’m posting below one such section. If more is required, please do say!

Here, then, is an excerpted “group” from my output:

:collide(spam): begin_group.
:collide(spam):   Considering render
  Comparing 0: bsphere, c (-50 -170 0), r 1 to bsphere, infinite, is_in = 1
:collide(spam):   render has 1 interested colliders ( 0. player )
:collide(spam):     Considering render/object
:collide(spam):     render/object has 0 interested colliders ( )
:collide(spam):     Considering render/fox rope
:collide(spam):     render/fox rope has 0 interested colliders ( )
:collide(spam):     Considering render/MeshDrawer
:collide(spam):     render/MeshDrawer has 0 interested colliders ( )
:collide(spam):     Considering render/MeshDrawer
:collide(spam):     render/MeshDrawer has 0 interested colliders ( )
:collide(spam):     Considering render/special effect
:collide(spam):     render/special effect has 0 interested colliders ( )
:collide(spam):     Considering render/special effect
:collide(spam):     render/special effect has 0 interested colliders ( )
:collide(spam):     Considering render/special effect
:collide(spam):     render/special effect has 0 interested colliders ( )
:collide(spam):     Considering render/special effect
:collide(spam):     render/special effect has 0 interested colliders ( )
:collide(spam):     Considering render/special effect
:collide(spam):     render/special effect has 0 interested colliders ( )
:collide(spam):     Considering render/special effect
:collide(spam):     render/special effect has 0 interested colliders ( )
:collide(spam):     Considering render/special effect
:collide(spam):     render/special effect has 0 interested colliders ( )
:collide(spam):     Considering render/MeshDrawer
:collide(spam):     render/MeshDrawer has 0 interested colliders ( )
:collide(spam):     Considering render/MeshDrawer
:collide(spam):     render/MeshDrawer has 0 interested colliders ( )
:collide(spam):     Considering render/MeshDrawer
:collide(spam):     render/MeshDrawer has 0 interested colliders ( )
:collide(spam):     Considering render/MeshDrawer
:collide(spam):     render/MeshDrawer has 0 interested colliders ( )
:collide(spam):     Considering render/MeshDrawer
:collide(spam):     render/MeshDrawer has 0 interested colliders ( )
:collide(spam):     Considering render/world root
    Comparing 0: bsphere, c (-50 -170 0), r 1 to bsphere, infinite, is_in = 1
:collide(spam):     render/world root has 1 interested colliders ( 0. player )
:collide(spam):       Considering render/world root/world
:collide(spam):       render/world root/world has 0 interested colliders ( )
:collide(spam):       Considering render/world root/room
:collide(spam):       render/world root/room has 0 interested colliders ( )
:collide(spam):       Considering render/world root/character-stopper
:collide(spam):       render/world root/character-stopper has 0 interested colliders ( )
:collide(spam):       Considering render/world root/level root
      Comparing 0: bsphere, c (-50 -170 0), r 1 to bsphere, c (-95.5371 -124.978 18.7533), r 180.591, is_in = 1
:collide(spam):       render/world root/level root has 1 interested colliders ( 0. player )
:collide(spam):         Considering render/world root/level root/room root
        Comparing 0: bsphere, c (-50 -170 0), r 1 to bsphere, c (-112.777 -124.978 18.7533), r 141.971, is_in = 1
:collide(spam):         render/world root/level root/room root has 1 interested colliders ( 0. player )
:collide(spam):           Considering render/world root/level root/room root/floor.007
:collide(spam):           render/world root/level root/room root/floor.007 has 0 interested colliders ( )
:collide(spam):           Considering render/world root/level root/room root/content.003
          Comparing 0: bsphere, c (3.85205 7.04082 0), r 1 to bsphere, c (-143.336 87.205 1.37091e-06), r 19.7317, is_in = 0
:collide(spam):           render/world root/level root/room root/content.003 has 0 interested colliders ( )
:collide(spam):           Considering render/world root/level root/room root/breakables
:collide(spam):           render/world root/level root/room root/breakables has 0 interested colliders ( )
:collide(spam):           Considering render/world root/level root/room root/wall.004
          Comparing 0: bsphere, c (3.85205 7.04082 0), r 1 to bsphere, c (-58.9245 58.0457 29.8), r 123.625, is_in = 1
:collide(spam):           render/world root/level root/room root/wall.004 has 1 interested colliders ( 0. player )
:collide(spam):             Considering render/world root/level root/room root/wall.004/Plane.243
:collide(spam):             render/world root/level root/room root/wall.004/Plane.243 has 0 interested colliders ( )
:collide(spam):             Considering render/world root/level root/room root/wall.004/Plane.242
:collide(spam):             render/world root/level root/room root/wall.004/Plane.242 has 0 interested colliders ( )
:collide(spam):             Considering render/world root/level root/room root/wall.004/Plane.030
            Comparing 0: bsphere, c (-12.6696 -10.1889 -29.8), r 1 to bsphere, c (-8.19937 -19.7821 -14.1658), r 17.8161, is_in = 0
:collide(spam):             render/world root/level root/room root/wall.004/Plane.030 has 0 interested colliders ( )
:collide(spam):             Considering render/world root/level root/room root/wall.004/Plane.008
            Comparing 0: bsphere, c (-12.6696 -10.1889 -29.8), r 1 to bsphere, c (12.3187 -19.7821 -14.1658), r 17.8161, is_in = 0
:collide(spam):             render/world root/level root/room root/wall.004/Plane.008 has 0 interested colliders ( )
:collide(spam):             Considering render/world root/level root/room root/wall.004/Plane.064
:collide(spam):             render/world root/level root/room root/wall.004/Plane.064 has 0 interested colliders ( )
:collide(spam):           Considering render/world root/level root/room root/object
          Comparing 0: bsphere, c (3.85205 7.04082 0), r 1 to bsphere, c (-10.7643 19.803 0), r 11.3212, is_in = 0
:collide(spam):           render/world root/level root/room root/object has 0 interested colliders ( )
:collide(spam):           Considering render/world root/level root/room root/object
          Comparing 0: bsphere, c (3.85205 7.04082 0), r 1 to bsphere, c (-11.7853 -22.7381 0), r 11.3212, is_in = 0
:collide(spam):           render/world root/level root/room root/object has 0 interested colliders ( )
:collide(spam):         Considering render/world root/level root/room root
        Comparing 0: bsphere, c (-50 -170 0), r 1 to bsphere, c (-3.59167 -188.47 -0.158312), r 67.2654, is_in = 1
:collide(spam):         render/world root/level root/room root has 1 interested colliders ( 0. player )
:collide(spam):           Considering render/world root/level root/room root/floor.006
:collide(spam):           render/world root/level root/room root/floor.006 has 0 interested colliders ( )
:collide(spam):           Considering render/world root/level root/room root/content.002
          Comparing 0: bsphere, c (-59.7543 11.4107 0), r 1 to bsphere, c (-12.1215 -40.1618 2.04146e-06), r 12.2658, is_in = 0
:collide(spam):           render/world root/level root/room root/content.002 has 0 interested colliders ( )
:collide(spam):           Considering render/world root/level root/room root/breakables
:collide(spam):           render/world root/level root/room root/breakables has 0 interested colliders ( )
:collide(spam):           Considering render/world root/level root/room root/wall.003
:collide(spam):           render/world root/level root/room root/wall.003 has 0 interested colliders ( )
:collide(spam):           Considering render/world root/level root/room root/environment
          Comparing 0: bsphere, c (-59.7543 11.4107 0), r 1 to bsphere, c (-42.7329 -8.46114 0), r 15.898, is_in = 0
:collide(spam):           render/world root/level root/room root/environment has 0 interested colliders ( )
:collide(spam):           Considering render/world root/level root/room root/environment
          Comparing 0: bsphere, c (-59.7543 11.4107 0), r 1 to bsphere, c (-42.7329 21.7228 0), r 9.84802, is_in = 0
:collide(spam):           render/world root/level root/room root/environment has 0 interested colliders ( )
:collide(spam):         Considering render/world root/level root/object
        Comparing 0: bsphere, c (-50 -170 0), r 1 to bsphere, c (-33.8719 -172.257 1.20501), r 5.35494, is_in = 0
:collide(spam):         render/world root/level root/object has 0 interested colliders ( )
:collide(spam):       Considering render/world root/celestial sphere
      Comparing 0: bsphere, c (-50 -170 0), r 1 to bsphere, infinite, is_in = 1
:collide(spam):       render/world root/celestial sphere has 1 interested colliders ( 0. player )
:collide(spam):         Considering render/world root/celestial sphere/sphere
        Comparing 0: bsphere, c (-50 -170 0), r 1 to bsphere, infinite, is_in = 1
:collide(spam):         render/world root/celestial sphere/sphere has 1 interested colliders ( 0. player )
:collide(spam): Colliding against CollisionNode 0x4ab9340:5 which has 1 collision solids.
:collide(spam):         Considering render/world root/celestial sphere/card root
:collide(spam):         render/world root/celestial sphere/card root has 0 interested colliders ( )
:collide(spam): end_group.

Looking over it, I see a number of non-collision-nodes mentioned. (One such is “render/fox rope”.) But does being “considered” mean that there’s actual overlap in the collision-mask?

And looking at the aforementioned “render/fox rope” node, it would appear that it has an “into” collide-mask that is all-off (and being a RopeNode, has no “from” collide-mask). So surely it shouldn’t be colliding with anything…?

[edit]
Further, I’ve tried a few recursive walks through the scene-graph, and I haven’t found any collision-nodes with the bit associated with visible geometry, not any non-collision nodes with any of the bits below that associated with visible geometry. :/

So, I’m a little stumped as to why I’m seeing PandaNodes turn up–and in number!–in my PStats cTrav listing… o_0 :/

On a related note, let me correct something that I said earlier: it appears that the “visible geometry” bit is in fact bit 20, and not bit 21.