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, 2016 NVIDIA Corporation. 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> <mdl_path> <bridge_server_url> <security_token> 013 // 014 // scene_file some scene file, e.g., main.mi 015 // mdl_path 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 mi::Sint32 result = neuray->start(); 336 check_start_success( result); 337 338 // Import the scene into the DB 339 import_and_store_scene( neuray, scene_file); 340 341 // Upload scene and render on the Bridge server, then export the rendered image to disk 342 rendering( neuray, bridge_server_url, security_token); 343 344 // Make a snapshot of the scene on the Bridge server 345 make_snapshot( neuray); 346 347 // Shut down the neuray library 348 check_success( neuray->shutdown() == 0); 349 neuray = 0; 350 351 // Unload the neuray library 352 check_success( unload()); 353 354 keep_console_open(); 355 return EXIT_SUCCESS; 356 }
example_bridge_server.cpp
001 /****************************************************************************** 002 * © 1986, 2016 NVIDIA Corporation. 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 mi::Sint32 result = neuray->start(); 142 check_start_success( result); 143 144 // Listen to Bridge clients 145 run_bridge_server( neuray, disk_cache_path, snapshot_path, bridge_server_address, 146 application_path, security_token, ssl_cert_file, ssl_private_key_file, ssl_password); 147 148 // Shut down the neuray library 149 check_success( neuray->shutdown() == 0); 150 neuray = 0; 151 152 // Unload the neuray library 153 check_success( unload()); 154 155 keep_console_open(); 156 return EXIT_SUCCESS; 157 }