Chapter 13. Multiprocessing

If you would like to display two views of the same scene graph, you need to use the multiprocessing capabilities of Cosmo 3D. You might, for example, like to display a ground-view and an over-head view of the same scene at the same time. You can accomplish this by creating a thread for each view and connecting each to their own csWindow and csContext, as shown in Figure 13-1.

Figure 13-1. Multiprocessing

Figure 13-1 Multiprocessing

This chapter describes how to implement multiprocessing in the following sections:

Implementing Multiprocessing

One rule that you must follow when implementing multiprocessing is the scene graph cannot be modified during rendering. Cosmo 3D does not provide a mechanism for concurrent scene graph modification and drawing.

The consequence of this rule is that you must implement semaphores to block the action of the application or draw threads.


Note: OpenGL Optimizer provides the semaphore class, opBarrier.

The following sections describe how to implement threads.

Creating Threads

For each view of the scene graph you want to display, you use a different thread. Each thread is bound to a csWindow and a csContext.

Threads must be created using csThread. Threads created in other ways produce unpredictable results.

A thread can only have one context attached to it at a time. Conversely, each thread that attempts to draw must have a separate context. Unpredictable results occur if you try to share a context with more than one thread.

A context attached to one thread cannot be used by another thread until the first thread releases it using csContext::releaseCurrent().

To create a thread and bind it appropriately, use the following procedure:

  1. Create a thread, as follows:

    csThread* drawThread = new csThread(drawThreadEntry, NULL);
    

  2. Bind the thread to a csWindow and csContext using csContext::makeCurrent(), as follows:

    static void
    drawThreadEntry(void* arg)
    {
    
        csWindow    *theWindow;
        csContext   *theContext;
    
        theContext = theWindow->getThisWindowContext();
    
        theContext->makeCurrent(theWindow);
        // Context is now current; scenes will be drawn in theWindow.
    ...
    }
    

Starting Threads

Whether or not a thread runs immediately depends on the csThread constructor you use. The first form, csThread(), does not start executing. This behavior allows you to load thread-related parameters before using csThread::start() to actually start the thread.

The second form of the constructor, csThread(Entry* entryPoint, void* arg=NULL), allows you to load thread-related parameters in the argument. For that reason, it begins execution immediately.

Thread Parameters

Thread-related parameters include the following csThread methods:

  • numProcessors()—returns the number of usable processors on the system.

  • setStackSize()—sets the stack size to be used for this thread when it starts.

Exiting Threads

A thread only terminates execution when it calls csThread::exit() on itself. Cosmo 3D does not support termination of threads by other threads.

Additional Thread Controls

The following csThread methods control the execution of threads:

  • sleep()—suspends the calling thread for the number of microseconds specified in the argument.

  • wait()—waits for this thread to terminate.

Thread Blocking

In general, a Cosmo 3D application has two kinds of threads:

  • The application thread, which handles events and creates draw threads.

  • Multiple draw threads, which render different views of the scene graph.

In general, when one kind of thread is active, the other must be blocked. So while the application thread handles events and modifies the scene graph, the draw threads must be blocked and, conversely, while the draw threads are active, the application thread must be blocked from modifying the scene graph, as shown in Figure 13-2.

Figure 13-2. Blocking Action of Multiple Threads

Figure 13-2 Blocking Action of Multiple Threads

The general order of events displayed in Figure 13-2 is as follows:

  1. The application and draw threads are created and initialized.

  2. The draw threads try to render but are stopped.

  3. The application thread modifies the scene graph.

  4. The scene graph is cleaned using csField::cleanFields().

  5. The application thread enters the Draw Barrier (semaphore) when it is ready to render the scenes.

  6. The application thread tries to run but is stopped by the Swap Barrier until all draw threads have run.

  7. The remainder of the draw threads run.

  8. The draw threads enter the Swap Barrier.

  9. when all threads enter the Swap Barrier they are released. The draw threads immediately enter the Draw Barrier and block.

  10. The application thread modifies the scene graph.

  11. The procedure repeats itself starting again at step 4.

Cleaning the csContext Fields

Before rendering, the application thread must call csField::cleanFields() to clean the csContext fields. This method forces the evaluation of all fields that have been dirty, thereby circumventing the normal lazy evaluation of field values. Generally, in Cosmo 3D, fields are updated only when queried.

To use this method, tracking of dirty fields must be enabled. This method enables safe traversal of a scene by parallel rendering threads; lazy evaluation is not thread-safe.

To clean the fields, you must:

  1. Enable cleaning by using the following method:

    csField::enableDirtyFieldTracking();
    

  2. Clean the fields by calling the following method each frame:

    csField::cleanFields();
    

To see if dirty field is enabled, use the following method:

csField::isDirtyFieldTrackingEnabled()

Multithreaded Example

For an example of a multi-threaded application, see twothread.cxx in .../optimizer1.1/demos/optimizer/misc/.