Cinema 4D Threads Manual

About

Cinema 4D performs multiple tasks in different threads. This way Cinema 4D can utilize multiple cores and can avoid that a single task blocks the application. Plugin code may be executed from the main thread or from some other threads. Depending on this thread context some operations are not allowed. All parts of the execution and drawing pipeline of Cinema 4D are threaded.

Main Thread

The main thread handles the Cinema 4D GUI, user interaction and the currently active document. It can spawn other threads like the rendering or viewport drawing thread.

The code of the following plugin classes is always executed from the main thread:

  • CommandData plugins
  • MessageData plugins
  • also Python scripts (i.e. executed from the Script Manager) are executed from the main thread (but not Python plugins).

Only the main thread is allowed to modify the currently active document and to perform the following operations:

  • Add an Event.
  • Make any changes to materials.
  • Change the structure of objects attached to the scene.
  • Change parameters of elements attached to the scene (allowed, but not recommended except for tags and objects following below rule (EXECUTIONPRIORITY_EXPRESSION), see Forbidden Actions).
  • Call a "Draw" function.
  • Perform any GUI functionality (e.g. displaying messages, opening dialogs etc.).
  • Create undos.
Warning
Don't do anything of the above list in any other thread than the main thread (like for example the execution and draw thread or any other default or custom thread).
Note
To check if the current thread is the main thread use GeIsMainThread(), see Thread Utility Manual.

It is possible to edit the active document from an asynchronous GeDialog. Before such an edit it is needed to call StopAllThreads() since there could be other threads reading the document.

Scene Execution Pipeline and Thread

Execution

A BaseDocument is composed of several elements like generators, deformers, tags, scene hooks etc. These elements can manipulate and change the scene. For example a tag may run its "Execute" function and a generator runs its "GetVirtualObjects" function. All these functions are called in a certain order in the execution pipeline.

The scene execution can be triggered in different situations:

  • EventAdd() is called. This will trigger DrawViews().
  • When DrawViews() is called it will start the execution of the active document. If DRAWFLAGS_NO_THREAD is not set the execution thread is started asynchronously. After the execution, the scene is drawn in the viewport.
  • BaseDocument::ExecutePasses() executes the document in the calling thread.
  • When the animation of the active document is played in the viewport, the scene is executed from the main thread.

When a document is executed the following happens:

  • Typically a pre pass builds a list of all objects and when they should be executed. "AddToExecution" is called on all elements that implement such a function.
  • The pipeline executes all elements of the scene depending on their priority.
  • After the execution of the active document the document is drawn in the editor viewport.

Priority

The order of execution depends on the priority of each element. If a plugin implements a "Execute" function the priority can be defined by implementing "AddToExecution". Many tags offer a "Priority" parameter that defines when they are executed.

Typical priority values are:

Plugin classes that offer a virtual "Execute" functions are:

Drawing

After the active document is executed it is typically drawn in the editor viewport. This means that the "Draw" function of all elements is called. More information on such "Draw" functions can be found in the Draw Manual.

Warning
It is not allowed to perform any file operations during drawing (these are allowed during execution, though).

Forbidden Actions

The execution and drawing pipeline is often executed in a thread. Other threads (like the manager redraws) access the document at the same time. That is why no scene modifications of any kind must be made in those routines. For example inserting an object into the scene is enough to trigger a crash.

As an exception, modifications are allowed that change the object's parameters like position, or anything set through NodeData::SetDParameter(). However this should only be done in Execute() by tags (expressions) and objects on EXECUTIONPRIORITY_EXPRESSION. In later stages of a generator object (e.g. EXECUTIONPRIORITY_GENERATOR) for example, this will lead to caches being rebuild constantly or mismatching data in Attribute Manager.

A common problem is to update/add materials in ObjectData::GetVirtualObjects(). This is the wrong place, materials cannot be created or changed in that function. Firstly, a redraw must never be triggered from within the redraw thread. Secondly, adding materials belongs (due to the scene access) into the user interaction part. For example, the materials needed for scene imports should be created already when the user imports the scene. ObjectData::GetVirtualObjects() is of course allowed to do ANY modifications that do not modify the scene: as the object returned is not in the scene at that time it may be changed/created in any way necessary.

Rendering Threads

Additional restrictions apply for functions that are called during rendering, e.g. ShaderData::Output() and MaterialData::CalcSurface(), since they can be run on multiple processors. No OS calls are allowed in these function.

Core Messages

If it is needed to modify the scene from a threaded function it is possible by sending a custom core message. Such a core message is sent with SpecialEventAdd() and can then be caught with a MessageData plugin. As MessageData::CoreMessage() runs in the context of the main tread, the active document can then be modified or GUI operations can be performed.

See the example in C4DThread Manual.

NodeData Plugins

NodeData based plugins typically become part of a BaseDocument. Their member functions are called when the document is executed and drawn. This execution can happen in a thread. An example is the "Render to Picture Viewer" process that renders a copy of the active document in the background. So one should never assume that any code of a NodeData based plugin is called in the context of the main thread.

Warning
Since NodeData based plugins may be executed in a thread it is forbidden to call StopAllThreads() in such a plugin.

Further Reading