1
$\begingroup$

I am using Blender 2.82a. My code seems to be working but creates "Dependency Cycle" messages from the command line, so I hope to understand why they are happening and if they are a cause of concern.

My particular use case is dropping a ball into a bag, where both the ball and bag (implemented as a half-sphere) have the cloth and collision modifiers, based on one of my answers here, but the use case is less important to me than understanding what causes dependency cycles. Here's what Blender looks like after running my Minimal Working Example:

enter image description here

Here is the Minimal Working Example script I am running:

import bpy

def clear_scene():
    """Clear existing objects in scene."""
    for block in bpy.data.meshes:
        if block.users == 0:
            bpy.data.meshes.remove(block)
    bpy.ops.object.mode_set(mode='OBJECT')
    bpy.ops.object.select_all(action='SELECT')
    bpy.ops.object.delete()

def make_plane():
    """Underlying plane. Use collision to support the fabrics."""
    bpy.ops.mesh.primitive_plane_add(location=(0, 0, 0))
    bpy.ops.transform.resize(value=(6.0, 6.0, 6.0))
    bpy.ops.object.modifier_add(type='COLLISION')

def make_object():
    """Make an object that goes inside the bag.

    We may only need the cloth modifier (collision might not be needed). Make
    sure the pressure is high to avoid the cloth 'ball' from collapsing.
    """
    bpy.ops.mesh.primitive_uv_sphere_add(radius=0.3, location=(0, 0, 2))
    bpy.ops.object.modifier_add(type='CLOTH')
    bpy.ops.object.modifier_add(type='COLLISION')
    bpy.context.object.modifiers["Cloth"].settings.quality = 10
    bpy.context.object.modifiers["Cloth"].collision_settings.collision_quality = 5
    bpy.context.object.modifiers["Cloth"].settings.use_pressure = True
    bpy.context.object.modifiers["Cloth"].settings.uniform_pressure_force = 10

def make_bag(radius=0.8, x=0.0, y=0.0, z=2.0, z_thresh=0.0):
    """We still use the cloth modifier, but the actual mesh is a UV sphere.

    Make the mesh at height z=2.0, then for any vertices with z values are
    *above* z_thresh, we delete. NOTE: it's actually local, so getting z
    coordinates of the sphere will still be 'centered' at 0. Also, adding
    collision modifier so that it can act as a 'bag' and support items.
    """
    bpy.ops.mesh.primitive_uv_sphere_add(radius=radius, location=(x, y, z))

    # Delete vertices that are ABOVE z_thresh.
    bpy.ops.object.mode_set(mode="EDIT")        # Activating Edit mode
    bpy.ops.mesh.select_all(action="DESELECT")  # Deselecting all
    bpy.ops.object.mode_set(mode="OBJECT")      # Going back to Object mode
    vert = bpy.context.object.data.vertices
    for v in vert:
        if v.co[2] >= z_thresh:
            v.select = True
    bpy.ops.object.mode_set(mode='EDIT')
    bpy.ops.mesh.delete(type='VERT')
    bpy.ops.object.mode_set(mode='OBJECT')

    # Add some cloth-related modifiers.
    bpy.ops.object.modifier_add(type='CLOTH')
    bpy.ops.object.modifier_add(type='COLLISION')
    bpy.context.object.modifiers["Cloth"].settings.quality = 10
    bpy.context.object.modifiers["Cloth"].collision_settings.collision_quality = 5
    bpy.context.object.modifiers["Cloth"].collision_settings.use_self_collision = True

if __name__ == '__main__':
    clear_scene()
    make_plane()
    # BOTH of the following lines must be un-commented to create dependency cycles.
    make_object()
    make_bag(radius=0.8, x=0.0, z=0.9, z_thresh=0.40)

If this is in a file called test-dependency.py then running blender -P test-dependency.py will produce the following:

$ blender -P test-dependency.py
Read prefs: /Users/anon/Library/Application Support/Blender/2.82/config/userpref.blend
found bundled python: /Applications/Blender.app/Contents/Resources/2.82/python
Warning: property 'release_confirm' not found in keymap item 'OperatorProperties'
Info: Deleted 3 object(s)
Dependency cycle detected:
  OBSphere.001/Geometry Component/GEOMETRY_EVAL() depends on
  OBSphere/Geometry Component/GEOMETRY_EVAL() via 'Cloth Collision'
  OBSphere.001/Geometry Component/GEOMETRY_EVAL() via 'Cloth Collision'
Dependency cycle detected:
  OBSphere.001/PointCache Component/POINT_CACHE_RESET() depends on
  OBSphere/Geometry Component/GEOMETRY_EVAL() via 'Point Cache'
  OBSphere.001/Geometry Component/GEOMETRY_EVAL() via 'Cloth Collision'
  OBSphere.001/PointCache Component/POINT_CACHE_RESET() via 'Point Cache -> Geometry'
Detected 2 dependency cycles

Both of the objects must be created for the dependency cycle to be created. Creating just the ball, or just the "sack", will not result in dependency cycles, interestingly. I am not sure why.

I am curious why these messages appear and if they are harmful. I'm having difficulty figuring this out based on various references. These are what Google shows me when I search for dependency cycles in Blender:

  • These questions: (question one, question two, question three) deal with dependency cycles with armatures. I am not using any armatures here and am not sure how to use insights from the answers there.
  • This question might be more relevant but doesn't have much information (and no answer).
  • This blog post looks like it might be useful for a high-level overview on dependencies, but is from 2014 (might be outdated) and again uses armatures.
  • There's a post from Blender Artists but looks even older (from 2009), and seems to suggest the warnings are important.
  • This Wiki reference perhaps provides the most comprehensive overview. It starts with saying that "The main goal of dependency graph is to make sure scene data is properly updated after any change in the most efficient way" ... so does that imply that it's an efficiency issue? If so then I'm probably fine with dependency cycles, since I have powerful machines that I can use.

Thanks for any feedback you may provide.

$\endgroup$
4
  • $\begingroup$ Seems like a limitation of the cloth simulation implementation T69727 that doesn't allow cloth with cloth collisions. I'm not familiar with that particular area of code to give a definitive answer on the spot though. $\endgroup$ – Robert Gützkow May 11 '20 at 19:13
  • 1
    $\begingroup$ That basically means : A is determined by B and B is determined by A, so the system can't be solved. For your particular use case, I don't know ; but it's a common problem with bone constraints and leads to unpredictable, "quirky" animations. $\endgroup$ – thibsert May 11 '20 at 21:44
  • $\begingroup$ I see, thanks @RobertGützkow and thibsert. From visually inspecting my animations, I don't currently see any weird physics. But it's hard to say. Perhaps it's harmless but could lead to weird animations that I just have to watch out for? $\endgroup$ – ComputerScientist May 12 '20 at 14:49
  • $\begingroup$ When I see Dependency cycle detected Blender starts to behave non-deterministic. It seems that actions that I do will be executed up to the point of dependency cycle and the rest of the action is silently ignored. This results in different glitches that look like major rendering errors. I think it may also cause corrupted .blend files but I'm not sure about that. $\endgroup$ – Mikko Rantalainen Oct 18 '20 at 18:06
3
$\begingroup$

In practice, having a dependency cycle means that Blender's internal evaluation order will matter and values may take a couple frames to converge on a valid solution, or diverge altogether.

E.G. If A=B+1 and B=A/2, then you have a dependency cycle of A—>B—>A.

If you start with A=4,B=4, then after one frame you could have either A=5,B=2.5 or B=2,A=3, depending on whether A or B gets evaluated first.

Usually it will eventually converge on a valid solution, like A=2,B=1, but some cases will diverge altogether.

Animated values will generally always lag behind by a couple of frames, because the values can only be evaluated using their results on the last frame.

For physics sims, I tentatively assume dependency cycles will be smoothed out by the solver iterations unless I can see a visible problem.


Explanation:

Having a dependency cycle means that somewhere in the Blender file is a circular sequence of values that all require each other to be calculated first, with no end, so there is a "Catch-22" in that there is no valid starting point for figuring out any of the values in the cycle.

A normal (non-circular) scene may go something like this:

Object1.location = Object2.location

Object2.location = Object3.location + 1

Object3.location = 5

In this example, because everything ultimately depends on Object3.location, there is a valid order in which the dependency graph can be traversed and the scene can be calculated. You can start with Object3.location, from that you figure out Object2.location, and from that you can figure out Object.location.

Once you step through and calculate all the steps of a non-cyclical dependency graph, all values will match their definitions.

But what happens if you have cyclical dependencies?

Object1.location = Object2.location + 1
OBject2.location = Object1.location - 3

In this example, because the two objects circularly depend on each other, there is no valid way to calculate the values in question and be sure they match how they're defined. You need to know Object2.location in order to figure out Object1.location, but you need to already know Object1.location in order to figure out Object2.location, so you're screwed, basically.

Under conditions like this, Blender will still try to figure out the required values by plugging in the last known values for their dependencies (usually from the previous frame). But it cannot guarantee that the results will be consistent with how the values are defined. In fact, in some cases like the above example, there may not be a consistent solution (or at least not one that can be found through naïve iteration).


Other MWEs:

You don't actually need to set up a physics sim for a MWE of a scene with a dependency cycle. IMO, the easiest way to create a dependency cycle is to just have two objects with constraints (or modifiers) that point towards each other. In this case, the transformation of the first object depends on the second object, but the second object depends on the first, so there is no valid starting point for calculating the scene. (Depending on the type of constraint, you may even be able to see the latency introduced by Blender's using values from the previous frame when you move one of the objects around.)

Or for an example that's easier to play with and dissect, you could also set up two drivers that both use the evaluated values from each other.

$\endgroup$
1
$\begingroup$

Will Chen's answer has some detailed info that I'm not going to repeat (on why a dependency cycle happens, what it means), but I think there are some remaining questions you have that I'll do my best to answer.

I'm also going to call dependency cycles "loops", because that's what they are: loops in controls. And because I think it makes it easier to think about them when using a plainer name, something less technical-sounding.

While it is possible to have loops-- Blender won't just error out on you-- it is almost never wise. It will make it irritating to animate anything. And you will probably end up with render/preview disagreement, which is maybe the worst thing in the world for a rendering engine where each frame could take hours to render.

However, that's talking about the general case of these dependency cycles, which are a lot more likely to come about via rigging or something. You've got a very specific kind of loop: a physics-collision loop. This is basically unavoidable when combining collision and physics (presumably, collision to affect other physical objects, which makes that loop clear: clothA depends on clothB which depends on clothA.)

Physics-collision loops have historically been okay, and have been used by a lot of Blender users to create interactions between physical objects. Physics create render/preview disagreement all on their own, until you bake physics-- at which point the loops disappear anyways. And if you're running physics, you're very specifically not hand animating stuff, so there's no difficulty in controlling the animation. They may even act at a higher frame rate (the physics subframe), thus "evening out" their dependency problems. I'm not sure; I haven't delved into the code or anything, and questions I've asked on devtalk regarding this particular problem have gone unanswered.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.