Iray Programmer's Manual

Bridge API

Iray Bridge is implemented on top of the Bridge API, a generic API that is part of the Iray API. This API allows third-party applications that embed Iray to create custom server-side Bridge applications that can be accessed by custom Bridge clients. The Bridge API provides all the core features used by Iray Bridge, such as efficient data transfer, server-side caching of data, video streaming, and so on.

example_bridge_client.cpp

001 /******************************************************************************
002  * © 1986, 2015 NVIDIA ARC GmbH. All rights reserved.
003  *****************************************************************************/
004 
005 // examples/example_bridge_client.cpp
006 //
007 // Connects to an Iray Bridge server, uploads a scene, makes a snapshot, and asks the server to
008 // render it.
009 //
010 // The example expects the following command line argument:
011 //
012 //   example_bridge_client <scene_file> <bridge_server_url> <security_token>
013 //
014 // scene_file              the path to a scene file
015 // mdl_path                to the MDL modules, e.g., iray-<version>/mdl
016 // bridge_server_url       URL of the Bridge server, e.g., ws://127.0.0.1:8998/iray (no SSL) or
017 //                         wss://127.0.0.1:8998/iray (SSL)
018 // security_token          security token to authenticate against the server
019 //
020 // The rendered image is written to a file named "example_bridge_client.png".
021 
022 #include <cstdio>
023 #include <sstream>
024 #include <string>
025 
026 #include <mi/neuraylib.h>
027 
028 // Include code shared by all examples.
029 #include "example_shared.h"
030 // Include an implementation of IRender_target.
031 #include "example_render_target_simple.h"
032 
033 
034 // Converts a number of bytes into a human-readable string.
035 std::string bytes_to_string( mi::Float64 size)
036 {
037     char buffer[256];
038     if( size > 1048576.0)
039         snprintf( buffer, sizeof( buffer)-1, "%.1f MB", size/1048576.0);
040     else if( size > 1024)
041         snprintf( buffer, sizeof( buffer)-1, "%.1f kB", size/1024.0);
042     else
043         snprintf( buffer, sizeof( buffer)-1, "%d bytes", static_cast<mi::Sint32>( size));
044     return buffer;
045 }
046 
047 // Progress callback implementation.
048 class Progress_callback :
049     public mi::base::Interface_implement<mi::neuraylib::IProgress_callback>
050 {
051 public:
052     void progress( mi::Float64 value, const char* area, const char* message)
053     {
054         if( strcmp( area, "bridge_bytes_uploaded") == 0)
055             m_bridge_bytes_uploaded = value;
056         else if( strcmp( area, "bridge_pending_cache_status") == 0)
057             m_bridge_pending_cache_status = value;
058         else if( strcmp( area, "bridge_pending_data_serialization") == 0)
059             m_bridge_pending_data_serialization = value;
060         else if( strcmp( area, "bridge_pending_hash_calculations") == 0)
061             m_bridge_pending_hash_calculations = value;
062         else if( strcmp( area, "bridge_total_bytes_to_upload") == 0)
063             m_bridge_total_bytes_to_upload = value;
064         else if( strcmp( area, "bridge_updated_elements") == 0)
065             m_bridge_updated_elements = value;
066         else if( strcmp( area, "bridge_upload_state") == 0)
067             m_bridge_upload_state = value;
068         else if( strcmp( area, "bridge_uploaded_element_bytes") == 0)
069             m_uploaded_element_uploaded_bytes = value;
070         else if( strcmp( area, "bridge_uploaded_element") == 0) {
071             if( message) {
072                 std::stringstream s;
073                 s << "\"" << message << "\"";
074                 m_uploaded_element_name = s.str();
075             } else
076                 m_uploaded_element_name = "unnamed element";
077             m_uploaded_element_size = value;
078         } else
079             return; // just print Bridge-related progress messages
080 
081         if( m_bridge_upload_state == 0) {
082 
083             fprintf( stderr, "Bridge detecting changes: %llu modified element(s) detected.\n",
084                 static_cast<mi::Size>( m_bridge_updated_elements));
085 
086         } else if( m_bridge_upload_state == 1) {
087 
088             fprintf( stderr, "Bridge calculating hashes: %llu pending calculation(s), "
089                 "%s uploaded.\n",
090                 static_cast<mi::Size>( m_bridge_pending_hash_calculations),
091                 bytes_to_string( m_bridge_bytes_uploaded).c_str());
092 
093         } else if( m_bridge_upload_state == 2) {
094 
095             fprintf( stderr, "Bridge querying cache status: %llu pending request(s), "
096                 "%s uploaded.\n",
097                 static_cast<mi::Size>( m_bridge_pending_cache_status),
098                 bytes_to_string( m_bridge_bytes_uploaded).c_str());
099 
100         } else if( m_bridge_upload_state == 3) {
101 
102             if( m_bridge_pending_data_serialization > 0.0)
103                 fprintf( stderr, "Bridge upload: %s/%s (%.0lf%%) total - data serialization for "
104                     "%llu element(s) pending.\n",
105                     bytes_to_string( m_bridge_bytes_uploaded).c_str(),
106                     bytes_to_string( m_bridge_total_bytes_to_upload).c_str(),
107                     (m_bridge_bytes_uploaded / m_bridge_total_bytes_to_upload * 100.0),
108                     static_cast<mi::Size>( m_bridge_pending_data_serialization));
109             else
110                 fprintf( stderr, "Bridge upload: %s/%s (%.0lf%%) total - %s/%s (%.0lf%%) for %s.\n",
111                     bytes_to_string( m_bridge_bytes_uploaded).c_str(),
112                     bytes_to_string( m_bridge_total_bytes_to_upload).c_str(),
113                     (m_bridge_bytes_uploaded / m_bridge_total_bytes_to_upload * 100.0),
114                     bytes_to_string( m_uploaded_element_uploaded_bytes).c_str(),
115                     bytes_to_string( m_uploaded_element_size).c_str(),
116                     (m_uploaded_element_uploaded_bytes / m_uploaded_element_size * 100.0),
117                     m_uploaded_element_name.c_str());
118 
119         } else if( m_bridge_upload_state == 4) {
120 
121             fprintf( stderr, "Bridge waiting for server to finish processing the upload.\n");
122 
123         } else if( m_bridge_upload_state == 5) {
124 
125             fprintf( stderr, "Bridge upload completed.\n");
126             m_bridge_upload_state = -1;
127 
128         }
129     }
130 
131 private:
132     mi::Float64   m_bridge_bytes_uploaded;
133     mi::Float64   m_bridge_pending_cache_status;
134     mi::Float64   m_bridge_pending_data_serialization;
135     mi::Float64   m_bridge_pending_hash_calculations;
136     mi::Float64   m_bridge_total_bytes_to_upload;
137     mi::Float64   m_bridge_updated_elements;
138     mi::Float64   m_bridge_upload_state;
139     MISTD::string m_uploaded_element_name;
140     mi::Float64   m_uploaded_element_size;
141     mi::Float64   m_uploaded_element_uploaded_bytes;
142 };
143 
144 // Snapshot callback implementation.
145 class Iray_bridge_snapshot_callback :
146     public mi::base::Interface_implement<mi::bridge::IIray_bridge_snapshot_callback>
147 {
148 public:
149     void ready( mi::Sint32 error_code, const char* /*file_name*/)
150     {
151         if( error_code == 0)
152             fprintf( stderr, "Successfully created cb snapshot.\n");
153         else
154             fprintf( stderr, "Failed to create cb snapshot.\n");
155         m_condition.signal();
156     }
157 
158     void progress( mi::Float64 value, const char* area, const char* message)
159     {
160         fprintf( stderr, "Progress: %.4f %s %s\n", value, area, message);
161     }
162 
163     void wait_for_ready_callback()
164     {
165         m_condition.wait();
166     }
167 
168 private:
169     mi::base::Condition m_condition;
170 };
171 
172 void configuration(
173     mi::base::Handle<mi::neuraylib::INeuray> neuray,
174     const char* mdl_path)
175 {
176     // Set the search path for .mdl files.
177     mi::base::Handle<mi::neuraylib::IRendering_configuration> rendering_configuration(
178         neuray->get_api_component<mi::neuraylib::IRendering_configuration>());
179     check_success( rendering_configuration.is_valid_interface());
180     check_success( rendering_configuration->add_mdl_path( mdl_path) == 0);
181 
182     // Load the FreeImage, Iray Bridge client, and .mi importer plugins.
183     mi::base::Handle<mi::neuraylib::IPlugin_configuration> plugin_configuration(
184         neuray->get_api_component<mi::neuraylib::IPlugin_configuration>());
185 #ifndef MI_PLATFORM_WINDOWS
186     check_success( plugin_configuration->load_plugin_library( "freeimage.so") == 0);
187     check_success( plugin_configuration->load_plugin_library( "iray_bridge_client.so") == 0);
188     check_success( plugin_configuration->load_plugin_library( "mi_importer.so") == 0);
189 #else
190     check_success( plugin_configuration->load_plugin_library( "freeimage.dll") == 0);
191     check_success( plugin_configuration->load_plugin_library( "iray_bridge_client.dll") == 0);
192     check_success( plugin_configuration->load_plugin_library( "mi_importer.dll") == 0);
193 #endif
194 }
195 
196 void import_and_store_scene(
197     mi::base::Handle<mi::neuraylib::INeuray> neuray,
198     const char* scene_file)
199 {
200     // Get the database, the global scope of the database, and create a transaction in the global
201     // scope.
202     mi::base::Handle<mi::neuraylib::IDatabase> database(
203         neuray->get_api_component<mi::neuraylib::IDatabase>());
204     check_success( database.is_valid_interface());
205     mi::base::Handle<mi::neuraylib::IScope> scope( database->get_global_scope());
206     mi::base::Handle<mi::neuraylib::ITransaction> transaction( scope->create_transaction());
207     check_success( transaction.is_valid_interface());
208 
209     // Import the scene file
210     mi::base::Handle<mi::neuraylib::IImport_api> import_api(
211         neuray->get_api_component<mi::neuraylib::IImport_api>());
212     check_success( import_api.is_valid_interface());
213     mi::base::Handle<const mi::IString> uri( import_api->convert_filename_to_uri( scene_file));
214     mi::base::Handle<const mi::neuraylib::IImport_result> import_result(
215         import_api->import_elements( transaction.get(), uri->get_c_str()));
216     check_success( import_result->get_error_number() == 0);
217 
218     // Create the scene object
219     mi::base::Handle<mi::neuraylib::IScene> scene(
220         transaction->create<mi::neuraylib::IScene>("Scene"));
221     check_success( scene.is_valid_interface());
222     scene->set_rootgroup(       import_result->get_rootgroup());
223     scene->set_options(         import_result->get_options());
224     scene->set_camera_instance( import_result->get_camera_inst());
225 
226     // And store it in the database
227     transaction->store( scene.get(), "the_scene");
228     scene = 0;
229     transaction->commit();
230 }
231 
232 void rendering(
233     mi::base::Handle<mi::neuraylib::INeuray> neuray,
234     const char* bridge_server_url,
235     const char* security_token)
236 {
237     // Configure the Iray Bridge client
238     mi::base::Handle<mi::bridge::IIray_bridge_client> iray_bridge_client(
239         neuray->get_api_component<mi::bridge::IIray_bridge_client>());
240     check_success( iray_bridge_client.is_valid_interface());
241     check_success( iray_bridge_client->set_application_url( bridge_server_url) == 0);
242     check_success( iray_bridge_client->set_security_token( security_token) == 0);
243 
244     // Get the database, the global scope of the database, and create a transaction in the global
245     // scope.
246     mi::base::Handle<mi::neuraylib::IDatabase> database(
247         neuray->get_api_component<mi::neuraylib::IDatabase>());
248     check_success( database.is_valid_interface());
249     mi::base::Handle<mi::neuraylib::IScope> scope( database->get_global_scope());
250     mi::base::Handle<mi::neuraylib::ITransaction> transaction( scope->create_transaction());
251     check_success( transaction.is_valid_interface());
252 
253     // Create the render context using the iray_cloud render mode.
254     mi::base::Handle<mi::neuraylib::IScene> scene(
255         transaction->edit<mi::neuraylib::IScene>( "the_scene"));
256     mi::base::Handle<mi::neuraylib::IRender_context> render_context(
257         scene->create_render_context( transaction.get(), "iray_cloud"));
258     check_success( render_context.is_valid_interface());
259     mi::base::Handle<mi::IString> scheduler_mode( transaction->create<mi::IString>());
260     scheduler_mode->set_c_str( "batch");
261     render_context->set_option( "scheduler_mode", scheduler_mode.get());
262     mi::base::Handle<mi::IString> video_format( transaction->create<mi::IString>());
263     video_format->set_c_str( "jpg");
264     render_context->set_option( "video_format", video_format.get());
265 
266     scene = 0;
267 
268     // Create the render target and render the scene
269     mi::base::Handle<mi::neuraylib::IImage_api> image_api(
270         neuray->get_api_component<mi::neuraylib::IImage_api>());
271     mi::base::Handle<mi::neuraylib::IRender_target> render_target(
272         new Render_target( image_api.get(), "Color", 512, 384));
273     mi::base::Handle<mi::neuraylib::IProgress_callback> callback(
274         new Progress_callback());
275     render_context->render( transaction.get(), render_target.get(), callback.get());
276 
277     // Write the image to disk
278     mi::base::Handle<mi::neuraylib::IExport_api> export_api(
279         neuray->get_api_component<mi::neuraylib::IExport_api>());
280     check_success( export_api.is_valid_interface());
281     mi::base::Handle<mi::neuraylib::ICanvas> canvas( render_target->get_canvas( 0));
282     export_api->export_canvas( "file:example_bridge_client.png", canvas.get());
283 
284     transaction->commit();
285 }
286 
287 void make_snapshot( mi::base::Handle<mi::neuraylib::INeuray> neuray)
288 {
289     // Get the database, the global scope of the database, and create a transaction in the global
290     // scope.
291     mi::base::Handle<mi::neuraylib::IDatabase> database(
292         neuray->get_api_component<mi::neuraylib::IDatabase>());
293     mi::base::Handle<mi::neuraylib::IScope> scope( database->get_global_scope());
294     mi::base::Handle<mi::neuraylib::ITransaction> transaction( scope->create_transaction());
295 
296     // Create a context for the snapshot.
297     mi::base::Handle<mi::bridge::IIray_bridge_client> iray_bridge_client(
298         neuray->get_api_component<mi::bridge::IIray_bridge_client>());
299     mi::base::Handle<mi::bridge::IIray_bridge_snapshot_context> snapshot_context(
300         iray_bridge_client->create_snapshot_context( transaction.get(), "the_scene"));
301 
302     // Create a callback instance, trigger snapshot creation, and wait for the callback to signal
303     // completion of the snapshot.
304     mi::base::Handle<Iray_bridge_snapshot_callback> callback( new Iray_bridge_snapshot_callback());
305     mi::Sint32 result
306         = snapshot_context->create_snapshot( transaction.get(), "snapshot.cb", callback.get());
307     if( result >= 0)
308         callback->wait_for_ready_callback();
309 
310     transaction->commit();
311 }
312 
313 int main( int argc, char* argv[])
314 {
315     // Collect command line parameters
316     if( argc != 5) {
317         fprintf( stderr,
318             "Usage: example_bridge_client <scene_file> <mdl_path> <bridge_url> <security_token>\n");
319         keep_console_open();
320         return EXIT_FAILURE;
321     }
322     const char* scene_file        = argv[1];
323     const char* mdl_path          = argv[2];
324     const char* bridge_server_url = argv[3];
325     const char* security_token    = argv[4];
326 
327     // Access the neuray library
328     mi::base::Handle<mi::neuraylib::INeuray> neuray( load_and_get_ineuray());
329     check_success( neuray.is_valid_interface());
330 
331     // Configure the neuray library
332     configuration( neuray, mdl_path);
333 
334     // Start the neuray library
335     check_success( neuray->start() == 0);
336 
337     // Import the scene into the DB
338     import_and_store_scene( neuray, scene_file);
339 
340     // Upload scene and render on the Bridge server, then export the rendered image to disk
341     rendering( neuray, bridge_server_url, security_token);
342 
343     // Make a snapshot of the scene on the Bridge server
344     make_snapshot( neuray);
345 
346     // Shut down the neuray library
347     check_success( neuray->shutdown() == 0);
348     neuray = 0;
349 
350     // Unload the neuray library
351     check_success( unload());
352 
353     keep_console_open();
354     return EXIT_SUCCESS;
355 }

example_bridge_server.cpp

001 /******************************************************************************
002  * © 1986, 2015 NVIDIA ARC GmbH. All rights reserved.
003  *****************************************************************************/
004 
005 // examples/example_bridge_server.cpp
006 //
007 // Starts listening for Iray Bridge clients. It will accept client rendering and snapshot requests.
008 //
009 // The example expects the following command line argument:
010 //
011 //   example_bridge_server <disk_cache_path> <snapshot_path> <bridge_server_address>
012 //                         <application_path> <security_token> [<ssl_cert_file>
013 //                         <ssl_private_key_file> <ssl_password>]
014 //
015 // disk_cache_path        directory where the server can cache uploaded elements
016 // snapshot_path          directory where the server can save snapshot files
017 // bridge_server_address  the address to listen on, e.g., 0.0.0.0:8998
018 // application_path       the application path used by the server, e.g., "/iray"
019 // security_token         security token to be provided by clients
020 // ssl_cert_file          file containing the server's SSL certificate
021 // ssl_priv_key_file      file containing the server's private key
022 // ssl_password           password for decrypting the server's private key
023 
024 #include <cstdio>
025 
026 #include <mi/neuraylib.h>
027 
028 // Include code shared by all examples.
029 #include "example_shared.h"
030 
031 // An application session handler that accepts all sessions that provide a given security token.
032 class Application_session_handler
033   : public mi::base::Interface_implement<mi::bridge::IApplication_session_handler>
034 {
035 public:
036     Application_session_handler( const std::string& security_token)
037       : m_security_token( security_token) { }
038 
039     bool on_session_connect( mi::bridge::IServer_session* session)
040     {
041         const char* security_token = session->get_security_token();
042         return security_token && m_security_token == security_token;
043 
044     }
045 private:
046     std::string m_security_token;
047 };
048 
049 void run_bridge_server(
050     mi::base::Handle<mi::neuraylib::INeuray> neuray,
051     const char* disk_cache_path,
052     const char* snapshot_path,
053     const char* bridge_server_address,
054     const char* application_path,
055     const char* security_token,
056     const char* ssl_cert_file,
057     const char* ssl_private_key_file,
058     const char* ssl_password)
059 {
060     // Start the HTTP server handling the Bridge connection using web sockets.
061     mi::base::Handle<mi::http::IFactory> http_factory(
062         neuray->get_api_component<mi::http::IFactory>());
063     mi::base::Handle<mi::http::IServer> http_server( http_factory->create_server());
064     if( ssl_cert_file && ssl_private_key_file && ssl_password)
065         http_server->start_ssl(
066             bridge_server_address, ssl_cert_file, ssl_private_key_file, ssl_password);
067     else
068         http_server->start( bridge_server_address);
069 
070     // Access the API component for the Iray Bridge server and create and application.
071     mi::base::Handle<mi::bridge::IIray_bridge_server> iray_bridge_server(
072         neuray->get_api_component<mi::bridge::IIray_bridge_server>());
073     check_success( iray_bridge_server.is_valid_interface());
074     mi::base::Handle<mi::bridge::IIray_bridge_application> iray_bridge_application(
075         iray_bridge_server->create_application( application_path, http_server.get()));
076 
077     if( iray_bridge_application.is_valid_interface()) {
078 
079         // Configure the application.
080         check_success( iray_bridge_application->set_disk_cache( disk_cache_path) == 0);
081         check_success( iray_bridge_application->set_snapshot_path( snapshot_path) == 0);
082         mi::base::Handle<mi::bridge::IApplication_session_handler> application_session_handler(
083             new Application_session_handler( security_token));
084         check_success( iray_bridge_application->set_session_handler(
085             application_session_handler.get()) == 0);
086 
087         // Run the Iray Bridge server for a fixed time span.
088         iray_bridge_application->open();
089         sleep_seconds( 60);
090         iray_bridge_application->close();
091     }
092 
093     http_server->shutdown();
094 }
095 
096 void configuration(
097     mi::base::Handle<mi::neuraylib::INeuray> neuray)
098 {
099     // Load the FreeImage, Iray Photoreal, and Iray Bridge server plugins.
100     mi::base::Handle<mi::neuraylib::IPlugin_configuration> plugin_configuration(
101         neuray->get_api_component<mi::neuraylib::IPlugin_configuration>());
102 #ifndef MI_PLATFORM_WINDOWS
103     check_success( plugin_configuration->load_plugin_library( "freeimage.so") == 0);
104     check_success( plugin_configuration->load_plugin_library( "libiray.so") == 0);
105     check_success( plugin_configuration->load_plugin_library( "iray_bridge_server.so") == 0);
106 #else
107     check_success( plugin_configuration->load_plugin_library( "freeimage.dll") == 0);
108     check_success( plugin_configuration->load_plugin_library( "libiray.dll") == 0);
109     check_success( plugin_configuration->load_plugin_library( "iray_bridge_server.dll") == 0);
110 #endif
111 }
112 
113 int main( int argc, char* argv[])
114 {
115     // Collect command line parameters
116     if( argc != 6 && argc != 9) {
117         fprintf( stderr, "Usage: example_bridge_server <disk_cache_path> <snapshot_path> "
118             "<bridge_server_address> \\\n");
119         fprintf( stderr, "  <application_path> <security_token> [<ssl_cert_file> "
120             "<ssl_private_key_file> <ssl_password>]\n");
121         keep_console_open();
122         return EXIT_FAILURE;
123     }
124     const char* disk_cache_path       = argv[1];
125     const char* snapshot_path         = argv[2];
126     const char* bridge_server_address = argv[3];
127     const char* application_path      = argv[4];
128     const char* security_token        = argv[5];
129     const char* ssl_cert_file         = argc >= 9 ? argv[6] : 0;
130     const char* ssl_private_key_file  = argc >= 9 ? argv[7] : 0;
131     const char* ssl_password          = argc >= 9 ? argv[8] : 0;
132 
133     // Access the neuray library
134     mi::base::Handle<mi::neuraylib::INeuray> neuray( load_and_get_ineuray());
135     check_success( neuray.is_valid_interface());
136 
137     // Configure the neuray library
138     configuration( neuray);
139 
140     // Start the neuray library
141     check_success( neuray->start() == 0);
142 
143     // Listen to Bridge clients
144     run_bridge_server( neuray, disk_cache_path, snapshot_path, bridge_server_address,
145         application_path, security_token, ssl_cert_file, ssl_private_key_file, ssl_password);
146 
147     // Shut down the neuray library
148     check_success( neuray->shutdown() == 0);
149     neuray = 0;
150 
151     // Unload the neuray library
152     check_success( unload());
153 
154     keep_console_open();
155     return EXIT_SUCCESS;
156 }