Manta architecture

From Manta Wiki
Jump to: navigation, search

Modular Architecture

Manta's modular components are organized into a rendering call stack of routines executed asynchronously within each thread and a multi-stage pipeline which contains points for synchronization. The simplest rendering pipeline contains two stages; image display and rendering.

Pipeline

One frame buffer is operated on by each stage in the pipeline. Threads synchronize for state changes between each stage. In the example pipeline, image display is the first stage, executed by only one thread, and the second stage is rendering. There is a one stage lag between rendering and image display so as the current frame is displayed, the next frame is rendered.

The simpliest Manta rendering pipeline consisting of two stages; image display, followed by rendering. Synchronization occurs between each stage.

Stack

During the rendering stage of the pipeline each thread asynchronously traverses the rendering stack. This call stack of modular components consists of an Image Traverser, Pixel Sampler, Renderer, and lastly scene graph).

The simpliest Manta rendering pipeline consisting of two stages; image display, followed by rendering. Each thread's execution of the stack is asynchronous during rendering

Ray Packets

Manta's highly modular architecture relies on virtual function calls to pass control between routines. Statically sized ray packets are used to pass ray tracing data up and down the call stack and help amortized the overhead of virtual function lookups.

Each ray packet includes a set of flags with general information about all of the rays it contains. These flags are used in the code to apply special case optimizations. For example if all of the rays in the packet have a common origin, an optimized version of the implicit sphere intersection routine may be used. Flags are also set after specific properties, such as inverse ray direction, are computed and stored for all of the ray packet elements.

In addition to amortizing the cost of virtual function lookups, ray packets increase opportunities for instruction level parallelism. Ray packets are laid out in memory as structures-of-arrays. This organization of member fields enables simple loading into SIMD registers.

Ray packets also increase memory access coherence during traversal, triangle intersection, and write-back to the frame buffer.

--Abe 01:21, 16 Feb 2006 (MST)

Transactions and Other Callbacks

Most state changes in manta are performed using transaction callbacks. These callbacks are executed each time a frame enters the pipeline. Other callbacks are scheduled to run at different times and are executed by one or more threads, depending on their type. Callbacks are messages, sent to Manta, which instructs it to call a method on an external object, with specified arguments.


</tr> </tr> </tr> </tr> </tr> </tr> </tr> </tr> </tr> </tr>
Manta Callbacks listed in the order they are executed by the rendering pipeline.
Callback Name Callback Function Type Description
Transactions CallbackBase_0Data* This is the most commonly used callback type. It is executed by thread 0 before the next frame begins rendering. Each transaction contains a flag indicating how the remaining transactions in the queue should be processed. By default all transactions in the queue are executed before the pipeline continues. The transaction queue is locked when the transaction is executed, so the callee should not add additional transactions to Manta.
One shot CallbackBase_2Data<int proc, int numProcs>* Executed by a thread 0 at a specific frame number.
Serial animation CallbackBase_3Data<int, int, bool&>* Contain single threaded code. Each callback is executed by a different thread in parallel. Third data argument is the changed flag which if set will trigger a preprocess of the channels.
Parallel One Shot CallbackBase_2Data<int, int>* Executed by all threads at the same time, at a specified frame number.
Parallel animation CallbackBase_3Data<int, int, bool&>* Contain parallel code, each callback is executed by every thread at the same time.
Image Display
Termination CallbackBase_1Data<MantaInterface *> Only executed by threads which are exiting. For example if the user decreases the number of rendering threads.
Serial pre-render CallbackBase_2Data<int, int>* Contain single threaded code. Each callback is executed by a different thread in parallel.
Parallel pre-render CallbackBase_2Data<int, int>* Contain parallel code, each callback is executed by every thread at the same time.
Image Traverser


The most common callbacks are transactions and one shot callbacks. Transactions are callbacks which change a small amount of state, such as the camera position as the user moves the mouse. Transactions are executed in a thread safe manner before the next frame enters the pipeline. One shot callbacks are very similar to transactions except that they can be scheduled to run at a specific time.

Transactions

Here is an example of how to send a transaction:

MantaInterface *manta_interface;

void FMantaWindow::onSetScene(...) { 
  Scene *scene;
  ... 
  manta_interface->addTransaction( "Set Scene",
    Callback::create( this, &FMantaWindow::mantaSetScene, scene ) );
}

void FMantaWindow::mantaSetScene( Scene *scene ) {
  manta_interface->setScene( scene );
}

When the GUI thread calls the onSetScene method, a transaction will be created which specified that the mantaSetScene method should be called on the FMantaWindow instance with the argument scene by the Manta thread.

Later, one of the Manta threads will call the method and pass it the stored argument. Note that the mantaSetScene method, which is a member of FMantaWindow, in general the method cannot safely update any state of the FMantaWindow instance since it is used by a separate, asynchronous, thread.

How Transactions Work

Transactions are stored in a mutex protected queue. By default, each time a frame enters the rendering pipeline, thread zero will execute each transaction in the list. This means that if a large number of small state changes (such as camera updates) build up in the transaction list, the aggerate effect of the small changes can be quite large.

Manta provides a set of flags which can be sent along with a transaction to specific how transaction processing should proceed after execution.

// Set the scene and process any remaining transactions in the queue.
manta_interface->addTransaction( "Set Scene",
  Callback::create( this, &FMantaWindow::mantaSetScene, scene ) );

// (same as)
manta_interface->addTransaction( "Set Scene",
  Callback::create( this, &FMantaWindow::mantaSetScene, scene ),
  TransactionBase::DEFAULT );

// Set the scene and continue with the rendering pipeline.
// Leave any remaining transactions in the queue until the next 
// frame.
manta_interface->addTransaction( "Set Scene",
  Callback::create( this, &FMantaWindow::mantaSetScene, scene )
  TransactionBase::CONTINUE );

// Set the scene and remove any remaining transactions from the
// queue.
manta_interface->addTransaction( "Set Scene",
  Callback::create( this, &FMantaWindow::mantaSetScene, scene ),
  TransactionBase::PURGE );

One Shot Callbacks

In the terminology of Interface/Callbacks.h, transactions are zero data callbacks, that means Manta will not set any arguments automatically when it invokes the callback. One shot callbacks, along with serial and parallel animation callbacks are two data callbacks. Manta will pass the number of the thread executing the callback, along with the total number of threads to the target method.

The frame number after which a callback should be executed is specified to be either relative or absolute.

Example One Shot Callback

Here is an excellent example of one shot callbacks: the -bench command line option in bin/manta.

The callback methods, themselves are quite straight forward, the first initializes a start time for the benchmark, and the second computes the elapsed time.

void BenchHelper::start(int, int) {
  startTime = Time::currentSeconds();
}

void BenchHelper::stop(int, int) {
  double dt = Time::currentSeconds()-startTime;
  double fps = numFrames/dt;
  if(verbose)
    cout << "Benchmark completed in " << dt 
         << " seconds (" << numFrames << " frames, " << fps 
         << "  frames per second)\n";
  else
    std::cout << fps << std::endl;
  rtrt->finish();
  delete this;
}

Next, one shots are added to Manta to invoke the above methods, at specific frame numbers.

BenchHelper* b = new BenchHelper( manta_interface, numFrames, verbose );

manta_interface->addOneShotCallback(
  MantaInterface::Absolute, warmup,
  Callback::create(b, &BenchHelper::start));
manta_interface->addOneShotCallback(
  MantaInterface::Absolute, warmup+numFrames,
  Callback::create(b, &BenchHelper::stop));

In this code, the one shots are always added to manta before rendering is started (effectively before frame number zero). If we wanted to use the benchmark helper after frame zero, we would specify MantaInterface::Relative and the current frame number would be added to the offset we specify.

Animation Callbacks

Serial or parallel animation callbacks are executed each time a frame enters the pipeline. Just like one shot callbacks, the executing thread number and total number of threads are passed as the second and third default arguments. The first default argument is a reference to a bool, which should be set to true if the animation callback changed some state.

Termination Callbacks

Termination callbacks are zero data callbacks (same as transactions) that are invoked when thread zero breaks out of the rendering pipeline. This may or may not be just before the entire program exits, depending on the behavior of the thread which calls MantaInterface::beginRendering.