Iray SDK API nvidia_logo_transpbg.gif Up
Example for the Bridge
[Previous] [Next] [Up]

This example demonstrate how to render a scene using the Bridge.

New Topics

  • Running a Bridge server
  • Using a Bridge server

Detailed Description

Running a Bridge server


This first example below shows how to run a Bridge server. The interface mi::bridge::IIray_bridge_server allows to create a Bridge application. To configure a Bridge application you need to set the port to listen on and the directories that should be used for the local disk cache and for snapshots.

Finally, you need to call mi::bridge::IIray_bridge_application::open() to actually start the Bridge server. It then accepts render requests from a Bridge client and snapshot requests of uploaded scenes.

Using a Bridge server


The second example below shows how to use a Bridge server. Again, you need to configure the address and port of the Bridge server to use. To use the Iray Bridge for an actual render request, you simple select the render mode "iray_cloud" instead of "iray". No other changes are required.

The example also shows how to create a snapshot of the uploaded scene on the server side. Note that this operation is asynchronous. In this example we simply wait for the operation to finish since there is nothing else to do. The created snapshot can be imported later using the cb_importer plugin (with the same setting for the cache directory on the server).

Example Source

Source Code Location: examples/example_bridge_server.cpp

/******************************************************************************
* Copyright 2023 NVIDIA Corporation. All rights reserved.
*****************************************************************************/
// examples/example_bridge_server.cpp
//
// Starts listening for Iray Bridge clients. It will accept client rendering and snapshot requests.
//
// The example expects the following command line argument:
//
// example_bridge_server <disk_cache_path> <snapshot_path> <bridge_server_address>
// <application_path> <security_token> [<ssl_cert_file>
// <ssl_private_key_file> <ssl_password>]
//
// disk_cache_path directory where the server can cache uploaded elements
// snapshot_path directory where the server can save snapshot files
// bridge_server_address the address to listen on, e.g., 0.0.0.0:8998
// application_path the application path used by the server, e.g., "/iray"
// security_token security token to be provided by clients
// ssl_cert_file file containing the server's SSL certificate
// ssl_priv_key_file file containing the server's private key
// ssl_password password for decrypting the server's private key
#include <iostream>
#include <mi/neuraylib.h>
// Include code shared by all examples.
#include "example_shared.h"
// An application session handler that accepts all sessions that provide a given security token.
class Application_session_handler
: public mi::base::Interface_implement<mi::bridge::IApplication_session_handler>
{
public:
Application_session_handler( const std::string& security_token)
: m_security_token( security_token) { }
bool on_session_connect( mi::bridge::IServer_session* session)
{
const char* security_token = session->get_security_token();
return security_token && m_security_token == security_token;
}
private:
std::string m_security_token;
};
void run_bridge_server(
const char* disk_cache_path,
const char* snapshot_path,
const char* bridge_server_address,
const char* application_path,
const char* security_token,
const char* ssl_cert_file,
const char* ssl_private_key_file,
const char* ssl_password)
{
// Start the HTTP server handling the Bridge connection using WebSockets.
mi::base::Handle<mi::http::IServer> http_server( http_factory->create_server());
if( ssl_cert_file && ssl_private_key_file && ssl_password)
http_server->start_ssl(
bridge_server_address, ssl_cert_file, ssl_private_key_file, ssl_password);
else
http_server->start( bridge_server_address);
// Access the API component for the Iray Bridge server and create and application.
check_success( iray_bridge_server.is_valid_interface());
iray_bridge_server->create_application( application_path, http_server.get()));
if( iray_bridge_application.is_valid_interface()) {
// Configure the application.
check_success( iray_bridge_application->set_disk_cache( disk_cache_path) == 0);
check_success( iray_bridge_application->set_snapshot_path( snapshot_path) == 0);
new Application_session_handler( security_token));
check_success( iray_bridge_application->set_session_handler(
application_session_handler.get()) == 0);
// Run the Iray Bridge server for a fixed time span.
iray_bridge_application->open();
sleep_seconds( 60);
iray_bridge_application->close();
}
http_server->shutdown();
}
void configuration( mi::neuraylib::INeuray* neuray)
{
// Load the OpenImageIO, Iray Photoreal, and Iray Bridge server plugins.
check_success( pc->load_plugin_library( "nv_openimageio" MI_BASE_DLL_FILE_EXT) == 0);
check_success( pc->load_plugin_library( "libiray" MI_BASE_DLL_FILE_EXT) == 0);
check_success( pc->load_plugin_library( "iray_bridge_server" MI_BASE_DLL_FILE_EXT) == 0);
}
int main( int argc, char* argv[])
{
// Collect command line parameters
if( argc != 6 && argc != 9) {
std::cerr << "Usage: example_bridge_server <disk_cache_path> <snapshot_path> \\\n"
<< " <bridge_server_address> <application_path> <security_token> \\\n"
<< " [<ssl_cert_file> <ssl_private_key_file> <ssl_password>]"
<< std::endl;
keep_console_open();
return EXIT_FAILURE;
}
const char* disk_cache_path = argv[1];
const char* snapshot_path = argv[2];
const char* bridge_server_address = argv[3];
const char* application_path = argv[4];
const char* security_token = argv[5];
const char* ssl_cert_file = argc >= 9 ? argv[6] : 0;
const char* ssl_private_key_file = argc >= 9 ? argv[7] : 0;
const char* ssl_password = argc >= 9 ? argv[8] : 0;
// Access the neuray library
mi::base::Handle<mi::neuraylib::INeuray> neuray( load_and_get_ineuray());
check_success( neuray.is_valid_interface());
// Configure the neuray library
configuration( neuray.get());
// Start the neuray library
mi::Sint32 result = neuray->start();
check_start_success( result);
// Listen to Bridge clients
run_bridge_server( neuray.get(), disk_cache_path, snapshot_path, bridge_server_address,
application_path, security_token, ssl_cert_file, ssl_private_key_file, ssl_password);
// Shut down the neuray library
check_success( neuray->shutdown() == 0);
neuray = 0;
// Unload the neuray library
check_success( unload());
keep_console_open();
return EXIT_SUCCESS;
}
Handle class template for interfaces, automatizing the lifetime control via reference counting.
Definition: handle.h:113
Mixin class template for deriving interface implementations.
Definition: interface_implement.h:41
API component that serves as entry point for the server-side Iray Bridge API.
Definition: iiray_bridge_server.h:221
Represents the server side of a Bridge session.
Definition: ibridge_server.h:523
virtual const char * get_security_token() const =0
Returns the security token specified by the client.
The factory can be used to instantiate the built-in HTTP classes.
Definition: http.h:922
This is an object representing the Iray library.
Definition: ineuray.h:44
virtual Sint32 shutdown(bool blocking=true)=0
Shuts down the library.
virtual base::IInterface * get_api_component(const base::Uuid &uuid) const =0
Returns an API component from the Iray SDK API.
virtual Sint32 start(bool blocking=true)=0
Starts the operation of the Iray library.
This interface is used to load plugins and to query information about loaded plugins.
Definition: iplugin_configuration.h:24
#define MI_BASE_DLL_FILE_EXT
The operating system specific default filename extension for shared libraries (DLLs)
Definition: config.h:340
signed int Sint32
32-bit signed integer.
Definition: types.h:46
Iray SDK API.

Source Code Location: examples/example_bridge_client.cpp

/******************************************************************************
* Copyright 2023 NVIDIA Corporation. All rights reserved.
*****************************************************************************/
// examples/example_bridge_client.cpp
//
// Connects to an Iray Bridge server, uploads a scene, makes a snapshot, and asks the server to
// render it.
//
// The example expects the following command line argument:
//
// example_bridge_client <scene_file> <mdl_path> <bridge_server_url> <security_token>
//
// scene_file some scene file, e.g., main.mi
// mdl_path path to the MDL modules, e.g., iray-<version>/mdl
// bridge_server_url URL of the Bridge server, e.g., ws://127.0.0.1:8998/iray (no SSL) or
// wss://127.0.0.1:8998/iray (SSL)
// security_token security token to authenticate against the server
//
// The rendered image is written to a file named "example_bridge_client.png".
#include <iostream>
#include <sstream>
#include <string>
#include <mi/neuraylib.h>
// Include code shared by all examples.
#include "example_shared.h"
// Include an implementation of IRender_target.
#include "example_render_target_simple.h"
// Converts a number of bytes into a human-readable string.
std::string bytes_to_string( mi::Float64 size)
{
char buffer[256];
if( size > 1048576.0)
snprintf( buffer, sizeof( buffer)-1, "%.1f MB", size/1048576.0);
else if( size > 1024)
snprintf( buffer, sizeof( buffer)-1, "%.1f kB", size/1024.0);
else
snprintf( buffer, sizeof( buffer)-1, "%d bytes", static_cast<mi::Sint32>( size));
return buffer;
}
// Progress callback implementation.
class Progress_callback
: public mi::base::Interface_implement<mi::neuraylib::IProgress_callback>
{
public:
void progress( mi::Float64 value, const char* area, const char* message)
{
if( strcmp( area, "bridge_bytes_uploaded") == 0)
m_bridge_bytes_uploaded = value;
else if( strcmp( area, "bridge_pending_cache_status") == 0)
m_bridge_pending_cache_status = value;
else if( strcmp( area, "bridge_pending_data_serialization") == 0)
m_bridge_pending_data_serialization = value;
else if( strcmp( area, "bridge_pending_hash_calculations") == 0)
m_bridge_pending_hash_calculations = value;
else if( strcmp( area, "bridge_total_bytes_to_upload") == 0)
m_bridge_total_bytes_to_upload = value;
else if( strcmp( area, "bridge_updated_elements") == 0)
m_bridge_updated_elements = value;
else if( strcmp( area, "bridge_upload_state") == 0)
m_bridge_upload_state = value;
else if( strcmp( area, "bridge_uploaded_element_bytes") == 0)
m_uploaded_element_uploaded_bytes = value;
else if( strcmp( area, "bridge_uploaded_element") == 0) {
if( message) {
std::stringstream s;
s << "\"" << message << "\"";
m_uploaded_element_name = s.str();
} else
m_uploaded_element_name = "unnamed element";
m_uploaded_element_size = value;
} else
return; // just print Bridge-related progress messages
if( m_bridge_upload_state == 0) {
fprintf( stderr, "Bridge detecting changes: %llu modified element(s) detected.\n",
static_cast<mi::Size>( m_bridge_updated_elements));
} else if( m_bridge_upload_state == 1) {
fprintf( stderr, "Bridge calculating hashes: %llu pending calculation(s), "
"%s uploaded.\n",
static_cast<mi::Size>( m_bridge_pending_hash_calculations),
bytes_to_string( m_bridge_bytes_uploaded).c_str());
} else if( m_bridge_upload_state == 2) {
fprintf( stderr, "Bridge querying cache status: %llu pending request(s), "
"%s uploaded.\n",
static_cast<mi::Size>( m_bridge_pending_cache_status),
bytes_to_string( m_bridge_bytes_uploaded).c_str());
} else if( m_bridge_upload_state == 3) {
if( m_bridge_pending_data_serialization > 0.0)
fprintf( stderr, "Bridge upload: %s/%s (%.0lf%%) total - data serialization for "
"%llu element(s) pending.\n",
bytes_to_string( m_bridge_bytes_uploaded).c_str(),
bytes_to_string( m_bridge_total_bytes_to_upload).c_str(),
(m_bridge_bytes_uploaded / m_bridge_total_bytes_to_upload * 100.0),
static_cast<mi::Size>( m_bridge_pending_data_serialization));
else
fprintf( stderr, "Bridge upload: %s/%s (%.0lf%%) total - %s/%s (%.0lf%%) for %s.\n",
bytes_to_string( m_bridge_bytes_uploaded).c_str(),
bytes_to_string( m_bridge_total_bytes_to_upload).c_str(),
(m_bridge_bytes_uploaded / m_bridge_total_bytes_to_upload * 100.0),
bytes_to_string( m_uploaded_element_uploaded_bytes).c_str(),
bytes_to_string( m_uploaded_element_size).c_str(),
(m_uploaded_element_uploaded_bytes / m_uploaded_element_size * 100.0),
m_uploaded_element_name.c_str());
} else if( m_bridge_upload_state == 4) {
fprintf( stderr, "Bridge waiting for server to finish processing the upload.\n");
} else if( m_bridge_upload_state == 5) {
fprintf( stderr, "Bridge upload completed.\n");
m_bridge_upload_state = -1;
}
}
private:
mi::Float64 m_bridge_bytes_uploaded;
mi::Float64 m_bridge_pending_cache_status;
mi::Float64 m_bridge_pending_data_serialization;
mi::Float64 m_bridge_pending_hash_calculations;
mi::Float64 m_bridge_total_bytes_to_upload;
mi::Float64 m_bridge_updated_elements;
mi::Float64 m_bridge_upload_state;
std::string m_uploaded_element_name;
mi::Float64 m_uploaded_element_size;
mi::Float64 m_uploaded_element_uploaded_bytes;
};
// Snapshot callback implementation.
class Iray_bridge_snapshot_callback
: public mi::base::Interface_implement<mi::bridge::IIray_bridge_snapshot_callback>
{
public:
void ready( mi::Sint32 error_code, const char* /*file_name*/)
{
if( error_code == 0)
fprintf( stderr, "Successfully created cb snapshot.\n");
else
fprintf( stderr, "Failed to create cb snapshot.\n");
m_condition.signal();
}
void progress( mi::Float64 value, const char* area, const char* message)
{
fprintf( stderr, "Progress: %.4f %s %s\n", value, area, message);
}
void wait_for_ready_callback()
{
m_condition.wait();
}
private:
mi::base::Condition m_condition;
};
void configuration( mi::neuraylib::INeuray* neuray, const char* mdl_path)
{
// Set the search path for .mdl files.
check_success( rc->add_mdl_path( mdl_path) == 0);
check_success( rc->add_mdl_path( ".") == 0);
// Load the OpenImageIO, Iray Bridge client, and .mi importer plugins.
check_success( pc->load_plugin_library( "nv_openimageio" MI_BASE_DLL_FILE_EXT) == 0);
check_success( pc->load_plugin_library( "iray_bridge_client" MI_BASE_DLL_FILE_EXT) == 0);
check_success( pc->load_plugin_library( "mi_importer" MI_BASE_DLL_FILE_EXT) == 0);
}
void import_and_store_scene( mi::neuraylib::INeuray* neuray, const char* scene_file)
{
// Get the database, the global scope of the database, and create a transaction in the global
// scope.
check_success( database.is_valid_interface());
mi::base::Handle<mi::neuraylib::IScope> scope( database->get_global_scope());
mi::base::Handle<mi::neuraylib::ITransaction> transaction( scope->create_transaction());
check_success( transaction.is_valid_interface());
// Import the scene file
check_success( import_api.is_valid_interface());
mi::base::Handle<const mi::IString> uri( import_api->convert_filename_to_uri( scene_file));
import_api->import_elements( transaction.get(), uri->get_c_str()));
check_success( import_result->get_error_number() == 0);
// Create the scene object
transaction->create<mi::neuraylib::IScene>( "Scene"));
check_success( scene.is_valid_interface());
scene->set_rootgroup( import_result->get_rootgroup());
scene->set_options( import_result->get_options());
scene->set_camera_instance( import_result->get_camera_inst());
// And store it in the database
transaction->store( scene.get(), "the_scene");
scene = 0;
transaction->commit();
}
void rendering(
mi::neuraylib::INeuray* neuray, const char* bridge_server_url, const char* security_token)
{
// Configure the Iray Bridge client
check_success( iray_bridge_client.is_valid_interface());
check_success( iray_bridge_client->set_application_url( bridge_server_url) == 0);
check_success( iray_bridge_client->set_security_token( security_token) == 0);
// Get the database, the global scope of the database, and create a transaction in the global
// scope.
check_success( database.is_valid_interface());
mi::base::Handle<mi::neuraylib::IScope> scope( database->get_global_scope());
mi::base::Handle<mi::neuraylib::ITransaction> transaction( scope->create_transaction());
check_success( transaction.is_valid_interface());
// Create the render context using the iray_cloud render mode.
transaction->edit<mi::neuraylib::IScene>( "the_scene"));
scene->create_render_context( transaction.get(), "iray_cloud"));
check_success( render_context.is_valid_interface());
mi::base::Handle<mi::IString> scheduler_mode( transaction->create<mi::IString>());
scheduler_mode->set_c_str( "batch");
render_context->set_option( "scheduler_mode", scheduler_mode.get());
mi::base::Handle<mi::IString> video_format( transaction->create<mi::IString>());
video_format->set_c_str( "jpg");
render_context->set_option( "video_format", video_format.get());
scene = 0;
// Create the render target and render the scene
new Render_target( image_api.get(), "Color", 512, 384));
new Progress_callback());
render_context->render( transaction.get(), render_target.get(), callback.get());
// Write the image to disk
check_success( export_api.is_valid_interface());
mi::base::Handle<mi::neuraylib::ICanvas> canvas( render_target->get_canvas( 0));
export_api->export_canvas( "file:example_bridge_client.png", canvas.get());
transaction->commit();
}
void make_snapshot( mi::neuraylib::INeuray* neuray)
{
// Get the database, the global scope of the database, and create a transaction in the global
// scope.
mi::base::Handle<mi::neuraylib::IScope> scope( database->get_global_scope());
mi::base::Handle<mi::neuraylib::ITransaction> transaction( scope->create_transaction());
// Create a context for the snapshot.
iray_bridge_client->create_snapshot_context( transaction.get(), "the_scene"));
// Create a callback instance, trigger snapshot creation, and wait for the callback to signal
// completion of the snapshot.
mi::base::Handle<Iray_bridge_snapshot_callback> callback( new Iray_bridge_snapshot_callback());
mi::Sint32 result
= snapshot_context->create_snapshot( transaction.get(), "snapshot.cb", callback.get());
if( result >= 0)
callback->wait_for_ready_callback();
transaction->commit();
}
int main( int argc, char* argv[])
{
// Collect command line parameters
if( argc != 5) {
std::cerr << "Usage: example_bridge_client <scene_file> <mdl_path> <bridge_url> \\\n"
" <security_token>" << std::endl;
keep_console_open();
return EXIT_FAILURE;
}
const char* scene_file = argv[1];
const char* mdl_path = argv[2];
const char* bridge_server_url = argv[3];
const char* security_token = argv[4];
// Access the neuray library
mi::base::Handle<mi::neuraylib::INeuray> neuray( load_and_get_ineuray());
check_success( neuray.is_valid_interface());
// Configure the neuray library
configuration( neuray.get(), mdl_path);
// Start the neuray library
mi::Sint32 result = neuray->start();
check_start_success( result);
// Import the scene into the DB
import_and_store_scene( neuray.get(), scene_file);
// Upload scene and render on the Bridge server, then export the rendered image to disk
rendering( neuray.get(), bridge_server_url, security_token);
// Make a snapshot of the scene on the Bridge server
make_snapshot( neuray.get());
// Shut down the neuray library
check_success( neuray->shutdown() == 0);
neuray = 0;
// Unload the neuray library
check_success( unload());
keep_console_open();
return EXIT_SUCCESS;
}
A simple string class.
Definition: istring.h:22
Conditions allow threads to signal an event and to wait for such a signal, respectively.
Definition: condition.h:33
API component that serves as entry point for the client-side Iray Bridge API.
Definition: iiray_bridge_client.h:248
This interface is used to interact with the distributed database.
Definition: idatabase.h:293
This interface is used to export files.
Definition: iexport_api.h:38
This interface provides various utilities related to canvases and buffers.
Definition: iimage_api.h:49
This interface is used to import files.
Definition: iimport_api.h:100
This interface is used to query and change the rendering configuration.
Definition: irendering_configuration.h:109
The scene is the top-level element describing a subset of DB elements to be rendered.
Definition: iscene.h:44
Uint64 Size
Unsigned integral type that is large enough to hold the size of all types.
Definition: types.h:112
double Float64
64-bit float.
Definition: types.h:52
[Previous] [Next] [Up]