Rendering is handled in a separate thread (plus more additional threads for parallel computation). This asynchronous process signals the main thread using various events. They are described briefly in the Getting Started We will list them here as well.
Renderer states
There are three main states with some variations:
- Idle
- Idle Initialized - The initial state before start() is called for the first time or after calling clearScene().
- Idle Stopped - The user stopped the renderer through stop() or through the VFB.
- Idle Error - An error caused the renderer to stop.
- Idle Done - The image completed rendering normally.
- Idle Frame Done - The current frame of an animation sequence completed normally and we're waiting for continueSequence() to be called.
- Preparing - Intermediate state after calling start() where V-Ray loads data and prepares its accelerating structures for rendering. The scene can not be modified while in this state - attempted changes will be discarded. DR hosts can't be added in this state either. With large and complex scenes it may take a while to get out of this state. This is why it is recommended to start() asynchronously. Otherwise the controlling thread will be blocked for a long time.
- Rendering
- Rendering Paused - Only possible in Interactive mode - Sampling has been paused, so the processor is idle, but nothing is unloaded from memory.
- Rendering Awaiting Changes - Only in Interactive mode and only when keepInteractiveRunning=true - Sampling stopped because it reached the desired quality level, but it will continue as soon as a change is applied to the scene. Unlike "Rendering Paused" this state is entered automatically.
In addition to the StateChanged event (see below) the VRayRenderer class provides a getState() (GetState() in .Net) method for checking what the current state is. Note that using getState() may cause race conditions in your business logic because the state changes asynchronously. In most cases it's best to use the StateChanged callback.
This is a diagram of the possible state transitions:
Core events
These are events common for all render modes:
- onStateChanged - Emitted every time the renderer enters a different rendering state (see above). The PREPARING state is important when using the recommended asynchronous renderer.start() method. In this case we avoid blocking the main thread, but we can't safely modify the renderer and scene contents. Many operations are blocked in PREPARING before entering the RENDERING state for this reason. You can only modify the scene after leaving the PREPARING state (interactive mode). Note that if you're rendering an animation sequence with renderSequence() there is an additional state between frames.
- onLogMessage - When V-Ray outputs text messages prior to and during rendering. This event logs informational messages as well as warnings and errors. It is highly recommended to always implement some kind of logging from this event. Diagnosing problems can be very hard without it.
- onProgress - When V-Ray switches the current task (e.g. loading, rendering) or reports increased progress percentage.
- onVFBClosed, onVFBRenderLast - V-Ray Frame Buffer related - when the VFB is closed and when the "Render last" button is pressed. We won't cover these in detail.
These events are specific only to Production mode when using the adaptive image sampler:
- onBucketInit, onBucketReady, onBucketFailed - An image "bucket" has started/finished/failed rendering. These can be used to highlight the currently processed part of the image in some way and also show which host is working on it in distributed rendering mode.
The following event is specific to Interactive mode and Production mode when using the progressive image sampler. Also preview updates from a light cache prepass may fire this event even in bucket production mode.
- onProgressiveImageUpdated - The rendered image is updated with the next sampling pass. This is usually used to display the latest results from the renderer in progressive mode when not relying on the VFB.
Performance and thread safety
It is important to keep in mind that events are dispatched in order from a dedicated AppSDK thread. If your callback code is synchronous and slow it will slow down the arrival of all events in the queue. So for example if you are doing some intensive image processing in the OnProgressiveImageUpdated callback, you should make it asynchronous. The fact that event callbacks are called from a separate thread also means that you need to be careful not to modify the VRayRenderer from the callback or guarantee that the main thread isn't modifying it at the same time. The renderer is not thread safe.
Multiple event handlers
Currently we allow only one handler to be assigned. To attach multiple callbacks, e.g. to log messages both to console and to a log file, just create a main event handler which calls all your actual implementations. The exceptions are .Net and Node.js which allow multiple handlers/listeners.
Removing an event handler
In addition to subscribing for an event, one can also remove the attached event handler. This is useful for instance for turning logging on/off. Events that occur while there is no event handler attached are not kept. You will not receive them when you set your handler later.
Lets see how we can add and then remove the message event handler:
def onMessage(renderer, message, level, instant): print(message) with vray.VRayRenderer() as renderer: renderer.setOnLogMessage(onMessage) # attach renderer.load("scene.vrscene") # this will log some output renderer.setOnLogMessage(None) # detach renderer.start() # no output during rendering renderer.waitForRenderEnd()
void onMessage(VRayRenderer& renderer, const char* message, MessageLevel level, double instant, void* userData) { printf("%s\n", message); } void main() { // ... renderer.setOnLogMessage(onMessage); // attach renderer.load("scene.vrscene"); // this will log some output renderer.setOnLogMessage(NULL); // detach renderer.start(); // no output during rendering renderer.waitForRenderEnd(); }
static void HandleMessageEvent(object source, VRay.MessageEventArgs e) { Console.WriteLine(e.Message); } static void Main(string[] args) { // ... renderer.LogMessage += new EventHandler<MessageEventArgs>(HandleMessageEvent); // attach renderer.Load("scene.vrscene"); // this will log some output renderer.LogMessage -= HandleMessageEvent; // detach renderer.Start(); // no output during rendering renderer.WaitForRenderEnd(); }
var onMessageHandler = function(message, level, instant) { console.log(message); }; renderer.on('logMessage', onMessageHandler); // attach renderer.load('scene.vrscene', function(err) { // this will log some output if (err) throw err; renderer.removeListener('logMessage', onMessageHandler); // detach renderer.start(function(err) { // no output during rendering if (err) throw err; renderer.waitForRenderEnd(function() { renderer.close(); }); }); });
More on the progress event
The progress event is common for all rendering engines. It signals progress on time consuming steps of the rendering process.
The handler receives a text description of the current task and two numbers: the number of abstract workunits already completed and the total workunits.
Here are advanced examples how to report progress in percentage and in addition to set the text and value to the VFB progress bar:
progressStart = 0.0 progressRelative = 0.0 elapsed = 0.0 remaining = 0.0 def printProgressExt(renderer, message, progress, total, instant): global progressStart global progressRelative global elapsed global remaining progressRelative = progress / total if (progress == 0): progressStart = instant elapsed = 0.0 remaining = 0.0 else: elapsed = instant - progressStart remainingMultiplier = (1.0 - progressRelative) / progressRelative remaining = elapsed * remainingMultiplier progressBarText = '{0} [{1:02d}:{2:02d}:{3:04.1f}][{4:02d}:{5:02d}:{6:04.1f} est]'.format(message, int(math.floor(elapsed / 3600.0)), int(math.floor(math.fmod(elapsed / 60.0, 60.0))), math.fmod(elapsed, 60.0), int(math.floor(remaining / 3600.0)), int(math.floor(math.fmod(remaining / 60.0, 60.0))), math.fmod(remaining, 60.0)) # Set the progress text and value to the VFB progress bar renderer.vfb.setProgressTextAndValue(progressBarText, progressRelative) print('Progress ({0}%) {1}'.format(round(100.0 * progressRelative, 2), progressBarText)) with vray.VRayRenderer() as renderer: renderer.renderMode = 'production' # A listener for progress event renderer.setOnProgress(printProgressExt) # Enable the progress bar in the VFB window. renderer.vfb.enableProgressBar() # render ...
char progressBarText[512]; double progressStart = 0.0; double progressRelative = 0.0; double elapsed = 0.0; double remaining = 0.0; void printProgressExt(VRayRenderer& renderer, const char* message, int progress, int total, double instant, void* userData) { progressRelative = double(progress) / double(total); if (progress == 0) { progressStart = instant; elapsed = 0.0; remaining = 0.0; } else { elapsed = instant - progressStart; const double remainingMultiplier = (1.0 - progressRelative) / progressRelative; remaining = elapsed * remainingMultiplier; } sprintf(progressBarText, "%s [%.2i:%.2i:%04.1f][%.2i:%.2i:%04.1f est]", message, int(floor(elapsed / 3600.0)), int(floor(fmod(elapsed / 60.0, 60.0))), fmod(elapsed, 60.0), int(floor(remaining / 3600.0)), int(floor(fmod(remaining / 60.0, 60.0))), fmod(remaining, 60.0)); // Set the progress text and value to the VFB progress bar renderer.vfb.setProgressTextAndValue(progressBarText, float(progressRelative)); printf("Progress (%5.2f%%) %s\n", 100.0 * progressRelative, progressBarText); } int main() { VRayInit init(NULL, true); VRayRenderer renderer; renderer.setRenderMode(VRayRenderer::RENDER_MODE_PRODUCTION); // A listener for progress event renderer.setOnProgress(printProgressExt); // Enable the progress bar in the VFB window renderer.vfb.enableProgressBar(); // render ... return 0; }
static string progressBarText; static double progressStart = 0.0; static double progressRelative = 0.0; static double elapsed = 0.0; static double remaining = 0.0; using (VRayRenderer renderer = new VRayRenderer()) { renderer.RenderMode = RenderMode.PRODUCTION; // A listener for progress event renderer.Progress += new EventHandler<ProgressEventArgs>((source, e) => { progressRelative = e.Progress / (double)e.Total; if (e.Progress == 0) { progressStart = e.Instant; elapsed = 0.0; remaining = 0.0; } else { elapsed = e.Instant - progressStart; double remainingMultiplier = (1.0 - progressRelative) / progressRelative; remaining = elapsed * remainingMultiplier; } progressBarText = string.Format("{0} [{1:00}:{2:00}:{3:00.0}][{4:00}:{5:00}:{6:00.0} est]", e.Message, Math.Floor(elapsed / 3600.0), Math.Floor((elapsed / 60.0) % 60.0), (elapsed % 60.0), Math.Floor(remaining / 3600.0), Math.Floor((remaining / 60.0) % 60.0), (remaining % 60.0) ); // Set the progress text and value to the VFB progress bar renderer.Vfb.SetProgressTextAndValue(progressBarText, (float)progressRelative); Console.WriteLine("Progress ({0,5:N2}%) {1}", 100.0 * progressRelative, progressBarText); }); // Enable the progress bar in the VFB window renderer.Vfb.EnableProgressBar(); // render ... }
var progressBarText; var progressStart = 0.0; var progressRelative = 0.0; var elapsed = 0.0; var remaining = 0.0; var renderer = vray.VRayRenderer(); renderer.renderMode = 'production'; // A listener for progress event renderer.on('progress', function(message, progress, total, instant) { progressRelative = progress / total; if (progress == 0) { progressStart = instant; elapsed = 0.0; remaining = 0.0; } else { elapsed = instant - progressStart; const remainingMultiplier = (1.0 - progressRelative) / progressRelative; remaining = elapsed * remainingMultiplier; } progressBarText = util.format('%s [%s:%s:%s][%s:%s:%s est]', message, Math.floor(elapsed / 3600.0).toString().padStart(2, '0'), Math.floor((elapsed / 60.0) % 60.0).toString().padStart(2, '0'), (elapsed % 60.0).toFixed(1).padStart(4, '0'), Math.floor(remaining / 3600.0).toString().padStart(2, '0'), Math.floor((remaining / 60.0) % 60.0).toString().padStart(2, '0'), (remaining % 60.0).toFixed(1).padStart(4, '0')); // Set the progress text and value to the VFB progress bar renderer.vfb.setProgressTextAndValue(progressBarText, progressRelative); console.log('Progress (' + (100.0 * progressRelative).toFixed(2) + '%) ' + progressBarText); }); // Enable the progress bar in the VFB window renderer.vfb.enableProgressBar(); // render ...