Hey @Aprecigout,
Welcome to the Maxon developers forum and its community, it is great to have you with us!
Getting Started
Before creating your next postings, we would recommend making yourself accustomed with our forum and support procedures. You did not do anything wrong, we point all new users to these rules.
- Forum Overview: Provides a broad overview of the fundamental structure and rules of this forum, such as the purpose of the different sub-forums or the fact that we will ban users who engage in hate speech or harassment.
- Support Procedures: Provides a more in detail overview of how we provide technical support for APIs here. This topic will tell you how to ask good questions and limits of our technical support.
- Forum Features: Provides an overview of the technical features of this forum, such as Markdown markup or file uploads.
It is strongly recommended to read the first two topics carefully, especially the section Support Procedures: How to Ask Questions.
About your First Question
Please have a look at Support Procedures: How to Ask Questions, there are too many question here. Which often derails threads, but since you are new, let's try it like this.
Is there a recommended way to flush or bypass the cache/memory buildup that seems to happen inside ExecutePasses, so execution time stays consistent?
Caches cannot be flushed, as they are a mandatory part of the scene graph. A scene always has exactly one cache per scene element which requires a cache. Without it is not operational. And while the term is effectively correct, the scene graph cache is not what you usually picture when talking about caches. Caches are the discrete embodiment of parametric objects and deformers. You can think of them as an invisible part of the scene hierarchy. With the 'Active Object' plugin from the C++ SDK we can have a peek at what cache means. Here we unfold the first cube object clone of the cloner in the cache.

In the next release of Cinema 4D there will also be mxutils.GetSceneGraphString
which has a similar purpose as the the plugin from the C++ SDK. For the scene shown above it will print what is shown in [1]. Everything below the [Cache]
of Cloner
is the cache of that scene element; a rather complex hidden hierarchy which itself contains caches which must be unpacked.
This also hints at the non-linear nature of caches. When you have a scene with 100 frames, where frame 0 is literally the empty scene and on frame 100 you have thousands of high resolution parametric objects which have to be rebuilt for this frame, and on top of that multiple complex simulations (pyro, particles, liquids), then executing the passes for frame 0
will be very quick as there is literally nothing to do, while executing the passes for frame 100
could cost seconds or even minutes (when none of the simulations are cached).
If such mechanisms exist, could someone outline the usual workflow or API calls to use—or point me to the relevant documentation?
Without knowing what you want to do, that is impossible to answer. Your code there could be slightly incorrect, as for 'pre-rolling' you usually want to execute the last pass twice, so that simulations can settle. When you just want to step through a scene, what you are doing is okay. It also should not make a big difference if your execute the passes for a scene state once ot twice, as all scene elements should only rebuild its caches when necessary when asked to do so. So, when you for example excute the passes and it takes 60 seconds, and then do it right again, the second run should only take a fraction of the first execution, as most scene elments should see that they are not dirty anymore, and just return their already existing cache.
But in general it is a bit odd that you execute the passes on all frames. It is very rare that you have to do that from Python. Maybe you could explain why you are doing that?
Finally, could C4DThread help in this context, or am I barking up the wrong tree? My experiments based on the thread linked above haven’t produced conclusive results.
The first argument of ExecutePasses
is the thread to which the call shall be bound. You can use this to make the pass execution non-blocking for the main thread.
This also hints at the broader answer. The pass execution is of course already heavily optimized and runs in as many threads as the machine can muster and things such as ObjectData::GetVirtualObjects
which are the backbone of cache building are run massively in parallel. The only thing you can decide is if you want to make your call non-blocking for the main thread or not (where the GUI code runs).
Not explicitly asked but sort of the elephant in the room: Executing the passes for all frames of a document varies drastically.
Just like many modern DCCs, Cinema 4D has a pretty sophisticated backed. Cinema 4D has for example on top of the "caching" of the scene graph a memoization core, which records the results of previous computations and reuses them when the same data is requested again. There is naturally some variance in such complex systems, where tiny changes in the input conditions can lead to significant differences in the execution time.
But what you show us there, that executing all passes of a scene takes twice or three times as long as the first time, is not normal. But I would at first be a bit doubtful that your findings are correct, as this would hint at a massive bug in the pass execution system. There could be an issue with the Python VM. I would recommend to unload the document in between the runs, to ensure that all possible memory is really freed.
Executing the passes for a single frame is already a not cheap operation, executing the passes for all frames of a document can be extensively expensive, since scene initialization makes up a good chunk of the render time of a document. So, doing this more than once in a row, is not the most clever thing. When you want to do this on multiple documents in a row, you should of course unload documents you are done with, so that you can free the memory.
Cheers,
Ferdinand
[1] Using print(mxutils.GetSceneGraphString(doc))
to visualize the content of a scene. Since we pass the whole document and not just the cloner, really everything gets unpacked here. Everything below the [Cache]
child of 'Cloner' (BaseObject: Omgcloner)
is the cache of the cloner object.
'' (BaseDocument: Tbasedocument)
├── [Branch] 'Objects' (Obase)
│ └── 'Cloner' (BaseObject: Omgcloner)
│ ├── [Cache]
│ │ └── 'Null' (BaseObject: Onull)
│ │ ├── 'Cube 0' (BaseObject: Ocube)
│ │ │ ├── [Cache]
│ │ │ │ └── 'Cube 0' (PolygonObject: Opolygon)
│ │ │ │ ├── [Deform Cache]
│ │ │ │ │ └── 'Cube 0' (PolygonObject: Opolygon)
│ │ │ │ │ └── [Branch] 'Tags' (Tbase)
│ │ │ │ │ ├── 'Phong' (BaseTag: Tphong)
│ │ │ │ │ ├── 'UVW' (UVWTag: Tuvw)
│ │ │ │ │ ├── '' (PolygonTag: Tpolygon)
│ │ │ │ │ └── '' (PointTag: Tpoint)
│ │ │ │ └── [Branch] 'Tags' (Tbase)
│ │ │ │ ├── 'Phong' (BaseTag: Tphong)
│ │ │ │ ├── 'UVW' (UVWTag: Tuvw)
│ │ │ │ ├── '' (PolygonTag: Tpolygon)
│ │ │ │ └── '' (PointTag: Tpoint)
│ │ │ ├── [Branch] 'Tags' (Tbase)
│ │ │ │ ├── 'Motion Graphics Color Tag' (BaseTag: Tmgcolor)
│ │ │ │ └── 'Phong' (BaseTag: Tphong)
│ │ │ └── 'Bend' (BaseObject: Obend)
│ │ ├── 'Sphere 1' (BaseObject: Osphere)
│ │ │ ├── [Cache]
│ │ │ │ └── 'Sphere 1' (PolygonObject: Opolygon)
│ │ │ │ ├── [Deform Cache]
│ │ │ │ │ └── 'Sphere 1' (PolygonObject: Opolygon)
│ │ │ │ │ └── [Branch] 'Tags' (Tbase)
│ │ │ │ │ ├── 'Phong' (BaseTag: Tphong)
│ │ │ │ │ ├── 'UVW' (UVWTag: Tuvw)
│ │ │ │ │ ├── '' (PolygonTag: Tpolygon)
│ │ │ │ │ └── '' (PointTag: Tpoint)
│ │ │ │ └── [Branch] 'Tags' (Tbase)
│ │ │ │ ├── 'Phong' (BaseTag: Tphong)
│ │ │ │ ├── 'UVW' (UVWTag: Tuvw)
│ │ │ │ ├── '' (PolygonTag: Tpolygon)
│ │ │ │ └── '' (PointTag: Tpoint)
│ │ │ ├── [Branch] 'Tags' (Tbase)
│ │ │ │ ├── 'Motion Graphics Color Tag' (BaseTag: Tmgcolor)
│ │ │ │ └── 'Phong' (BaseTag: Tphong)
│ │ │ └── 'Bend' (BaseObject: Obend)
│ │ ├── 'Cube 2' (BaseObject: Ocube)
│ │ │ ├── [Cache]
│ │ │ │ └── 'Cube 2' (PolygonObject: Opolygon)
│ │ │ │ ├── [Deform Cache]
│ │ │ │ │ └── 'Cube 2' (PolygonObject: Opolygon)
│ │ │ │ │ └── [Branch] 'Tags' (Tbase)
│ │ │ │ │ ├── 'Phong' (BaseTag: Tphong)
│ │ │ │ │ ├── 'UVW' (UVWTag: Tuvw)
│ │ │ │ │ ├── '' (PolygonTag: Tpolygon)
│ │ │ │ │ └── '' (PointTag: Tpoint)
│ │ │ │ └── [Branch] 'Tags' (Tbase)
│ │ │ │ ├── 'Phong' (BaseTag: Tphong)
│ │ │ │ ├── 'UVW' (UVWTag: Tuvw)
│ │ │ │ ├── '' (PolygonTag: Tpolygon)
│ │ │ │ └── '' (PointTag: Tpoint)
│ │ │ ├── [Branch] 'Tags' (Tbase)
│ │ │ │ ├── 'Motion Graphics Color Tag' (BaseTag: Tmgcolor)
│ │ │ │ └── 'Phong' (BaseTag: Tphong)
│ │ │ └── 'Bend' (BaseObject: Obend)
│ │ ├── 'Sphere 3' (BaseObject: Osphere)
│ │ │ ├── [Cache]
│ │ │ │ └── 'Sphere 3' (PolygonObject: Opolygon)
│ │ │ │ ├── [Deform Cache]
│ │ │ │ │ └── 'Sphere 3' (PolygonObject: Opolygon)
│ │ │ │ │ └── [Branch] 'Tags' (Tbase)
│ │ │ │ │ ├── 'Phong' (BaseTag: Tphong)
│ │ │ │ │ ├── 'UVW' (UVWTag: Tuvw)
│ │ │ │ │ ├── '' (PolygonTag: Tpolygon)
│ │ │ │ │ └── '' (PointTag: Tpoint)
│ │ │ │ └── [Branch] 'Tags' (Tbase)
│ │ │ │ ├── 'Phong' (BaseTag: Tphong)
│ │ │ │ ├── 'UVW' (UVWTag: Tuvw)
│ │ │ │ ├── '' (PolygonTag: Tpolygon)
│ │ │ │ └── '' (PointTag: Tpoint)
│ │ │ ├── [Branch] 'Tags' (Tbase)
│ │ │ │ ├── 'Motion Graphics Color Tag' (BaseTag: Tmgcolor)
│ │ │ │ └── 'Phong' (BaseTag: Tphong)
│ │ │ └── 'Bend' (BaseObject: Obend)
│ │ └── 'Cube 4' (BaseObject: Ocube)
│ │ ├── [Cache]
│ │ │ └── 'Cube 4' (PolygonObject: Opolygon)
│ │ │ ├── [Deform Cache]
│ │ │ │ └── 'Cube 4' (PolygonObject: Opolygon)
│ │ │ │ └── [Branch] 'Tags' (Tbase)
│ │ │ │ ├── 'Phong' (BaseTag: Tphong)
│ │ │ │ ├── 'UVW' (UVWTag: Tuvw)
│ │ │ │ ├── '' (PolygonTag: Tpolygon)
│ │ │ │ └── '' (PointTag: Tpoint)
│ │ │ └── [Branch] 'Tags' (Tbase)
│ │ │ ├── 'Phong' (BaseTag: Tphong)
│ │ │ ├── 'UVW' (UVWTag: Tuvw)
│ │ │ ├── '' (PolygonTag: Tpolygon)
│ │ │ └── '' (PointTag: Tpoint)
│ │ ├── [Branch] 'Tags' (Tbase)
│ │ │ ├── 'Motion Graphics Color Tag' (BaseTag: Tmgcolor)
│ │ │ └── 'Phong' (BaseTag: Tphong)
│ │ └── 'Bend' (BaseObject: Obend)
│ ├── [Branch] 'Tags' (Tbase)
│ │ └── 'Info' (BaseTag: ID_MOTAGDATA)
│ ├── 'Cube' (BaseObject: Ocube)
│ │ ├── [Branch] 'Tags' (Tbase)
│ │ │ └── 'Phong' (BaseTag: Tphong)
│ │ └── 'Bend' (BaseObject: Obend)
│ └── 'Sphere' (BaseObject: Osphere)
│ ├── [Branch] 'Tags' (Tbase)
│ │ └── 'Phong' (BaseTag: Tphong)
│ └── 'Bend' (BaseObject: Obend)
├── [Branch] 'Render Settings' (Rbase)
│ └── 'My Render Setting' (RenderData: Rbase)
│ ├── [Branch] 'Post Effects' (VPbase)
│ │ ├── 'Magic Bullet Looks' (BaseVideoPost: VPMagicBulletLooks)
│ │ └── 'Redshift' (BaseVideoPost: VPrsrenderer)
│ └── [Branch] 'Multi-Pass' (Zmultipass)
│ └── 'Post Effects' (BaseList2D: Zmultipass)
├── [Branch] 'Scene Hooks' (SHplugin)
│ ├── 'STHOOK' (BaseList2D: 1012061)
│ ├── 'RSCameraObjectTargetDistancePicker' (BaseList2D: 31028063)
│ ├── 'Python Embedded Change Monitor' (BaseList2D: 1058422)
│ ├── 'SceneHook' (BaseList2D: 1028481)
│ ├── 'CmSceneHook' (BaseList2D: 1026839)
│ ├── 'CameraMorphDrawSceneHook' (BaseList2D: 1029281)
│ ├── 'MotionCameraDrawSceneHook' (BaseList2D: 1029338)
│ ├── 'USD Scene Hook' (BaseList2D: 1055307)
│ ├── 'Substance Assets' (BaseList2D: 1032107)
│ ├── 'Alembic Archive Hook' (BaseList2D: 1028458)
│ ├── 'UpdateMerge Hook' (BaseList2D: 465001602)
│ ├── 'ArchiExchangeCADHook' (BaseList2D: 200000216)
│ ├── 'SLA wave scene hook' (BaseList2D: REG_EXP_PARSER)
│ ├── 'Thinking Particles' (TP_MasterSystem: ID_THINKINGPARTICLES)
│ ├── '' (BaseList2D: 1035577)
│ ├── 'Bullet' (BaseList2D: 180000100)
│ ├── 'XRefs' (BaseList2D: 1025807)
│ ├── 'CAManagerHook' (BaseList2D: 1019636)
│ │ └── [Branch] 'Weights Handler Head' (Tbaselist2d)
│ │ └── 'Weights Handler' (BaseList2D: 1037891)
│ ├── 'Volume Save Manager Hook' (BaseList2D: 1040459)
│ ├── 'UV Display 3D SceneHook' (BaseList2D: 1054166)
│ ├── 'uvhook' (BaseList2D: 1053309)
│ ├── 'ScatterPlacementHook' (BaseList2D: 1058060)
│ ├── 'Tool System Hook' (BaseList2D: ID_TOOL_SYSTEM_HOOK)
│ │ └── [Branch] 'SBM' (431000215)
│ │ └── 'Symmetry node' (BaseList2D: 431000215)
│ │ └── [Branch] 'C4DCoreWrapper' (200001044)
│ │ └── 'Symmetry node - net.maxon.symmetry.context.modeling' (BaseList2D: 300001078)
│ ├── 'MoGraphSceneHook' (BaseList2D: 1019525)
│ ├── 'gozScenehook' (BaseList2D: 1059748)
│ ├── 'Simulation' (BaseList2D: ID_SIMULATIONSCENE_HOOK)
│ │ └── [Branch] 'Simulation World' (Obase)
│ │ └── 'Default Simulation Scene' (BaseObject: Osimulationscene)
│ ├── 'PersistentHook' (BaseList2D: 180420202)
│ ├── 'Scene Nodes' (BaseList2D: SCENENODES_IDS_SCENEHOOK_ID)
│ ├── 'NE_SceneHook' (BaseList2D: 465002367)
│ ├── 'Take Hook' (BaseList2D: 431000055)
│ │ └── [Branch] 'Take System Branch' (TakeBase)
│ │ └── 'Main' (BaseTake: TakeBase)
│ │ └── [Branch] 'Override Folders' (431000073)
│ │ └── 'Overrides' (BaseList2D: 431000073)
│ │ ├── 'Others' (BaseList2D: 431000073)
│ │ ├── 'Layers' (BaseList2D: 431000073)
│ │ ├── 'Materials' (BaseList2D: 431000073)
│ │ ├── 'Shaders' (BaseList2D: 431000073)
│ │ ├── 'Tags' (BaseList2D: 431000073)
│ │ └── 'Objects' (BaseList2D: 431000073)
│ ├── 'CombineAc18_AutoCombine_SceneHook' (BaseList2D: 1032178)
│ ├── 'PLKHUD' (BaseList2D: 1020132)
│ │ └── [Branch] 'PSUNDOHEAD' (Obase)
│ │ └── 'PKHOP' (BaseObject: 1020120)
│ ├── 'RenderManager Hook' (BaseList2D: 465003509)
│ ├── 'Sound Scrubbing Hook' (BaseList2D: 100004815)
│ ├── 'To Do' (BaseList2D: 465001536)
│ ├── 'Animation' (BaseList2D: 465001535)
│ ├── 'BaseSettings Hook' (BaseList2D: ID_BS_HOOK)
│ ├── '' (BaseList2D: 1060457)
│ ├── 'SculptBrushModifierSceneHook' (BaseList2D: 1030499)
│ ├── 'Sculpt Objects' (BaseList2D: 1024182)
│ ├── 'HairHighlightHook' (BaseList2D: 1018870)
│ ├── 'MeshObject Scene Hook' (BaseList2D: 1037041)
│ ├── 'Lod Hook' (BaseList2D: 431000182)
│ ├── 'Annotation Tag SceneHook' (BaseList2D: 1030679)
│ ├── 'Sniper' (BaseList2D: 430000000)
│ ├── 'Mesh Check Hook' (BaseList2D: 431000027)
│ ├── 'Modeling Objects Hook' (BaseList2D: 431000032)
│ │ └── [Branch] 'Modeling Objects Branch' (431000031)
│ │ ├── 'Pattern Direction Manipulator' (BaseObject: Opatternmanipulator)
│ │ ├── 'Plane Manipulator' (BaseObject: Oplanemanipulator)
│ │ ├── 'Pivot Manipulator' (BaseObject: Opivotmanipulator)
│ │ ├── 'Knife Line Manipulator' (BaseObject: 431000168)
│ │ ├── 'Subdivision Manipulator' (BaseObject: 431000172)
│ │ └── 'PolyPenObject' (BaseObject: 431000031)
│ ├── 'Snap Scenehook' (BaseList2D: 440000111)
│ │ ├── [Branch] 'WpSH' (440000111)
│ │ │ └── 'WorkPlane' (BaseObject: Oworkplane)
│ │ └── [Branch] 'MdSH' (Tbase)
│ │ └── 'Modeling Settings' (BaseList2D: 440000140)
│ ├── 'Doodle Hook' (BaseList2D: 1022212)
│ ├── 'Stereoscopic' (BaseList2D: 450000226)
│ ├── 'ViewportExtHookHUD' (BaseList2D: ID_VIEW_SCENEHOOKHUD)
│ ├── 'ViewportExtHookhighlight' (BaseList2D: ID_VIEW_SCENEHOOKHIGHLIGHT)
│ ├── 'MeasureSceneHook' (BaseList2D: ID_MEASURE_SCENEHOOK)
│ ├── 'Redshift' (BaseList2D: 1036748)
│ ├── 'GvHook' (BaseList2D: ID_SCENEHOOK_PLUGIN)
│ ├── 'Material Scene Hook' (BaseList2D: 300001077)
│ ├── 'TargetDistancePicker' (BaseList2D: 1028063)
│ └── 'BodyPaint SceneHook' (BaseList2D: 1036428)
└── [Branch] '' (Tbasedraw)
└── '' (BaseList2D: 110306)