NVIDIA Index example code nvidia_logo_transpbg.gif Up
distributed_heightfield_data.cpp
Go to the documentation of this file.
1/******************************************************************************
2 * Copyright 2023 NVIDIA Corporation. All rights reserved.
3 *****************************************************************************/
6
7#include <iostream>
8#include <limits>
9#include <sstream>
10#include <vector>
11
12#include <mi/dice.h>
13
14// Include code shared by all examples.
15#include "utility/example_shared.h"
16
17#include <nv/index/icamera.h>
18#include <nv/index/iconfig_settings.h>
19#include <nv/index/idistributed_data_access.h>
20#include <nv/index/iindex.h>
21#include <nv/index/ilight.h>
22#include <nv/index/imaterial.h>
23#include <nv/index/iscene.h>
24#include <nv/index/isession.h>
25
26#include <nv/index/app/index_connect.h>
27#include <nv/index/app/string_dict.h>
28#include <nv/index/app/idata_analysis_and_processing.h>
29
30/*
31
32General Distributed Computing Concepts
33======================================
34NVIDIA IndeX distributes data in the cluster environment according to some distribution scheme.
35As a result, the data cannot be accessed nor edited locally on a single machine.
36NVIDIA IndeX has introduced general concepts that allow determining the locality of
37distributed data as well as accessing and editing the distributed data.
38
39The data locality, access and editing paradigms enable the implementation of arbitrary compute
40algorithms, such as auto-tracking techniques, which are primarily based on data hosted by the
41rendering cluster. In addition, user-defined query methods, such as snapping techniques, or
42data export schemes can be implemented efficiently. The general distributed computing concepts
43typically rely on a 3D query region (3D bounding box), which an application developer can define
44for his purposes.
45
46
47Data Locality
48=============
49The data locality provides the application the information where, i.e., on which machine in the
50cluster, the actual data of a distributed dataset is stored. A single cluster machine stores either
51none or subsets of the entire data. Furthermore, the data locality information provides the corresponding
523D bounding boxes of each subset. Each of the 3D bounding boxes can be accessed by means of an index
53and the respective index also corresponds to a subset.
54
55A common use case that requires the data locality is the invocation of parallel and distributed compute tasks.
56Based on the data locality, the compute tasks can be directed to the machines that actually store the
57distributed data allowing local computation on the data without transferring the data through the network.
58
59Distributed Data Access
60=======================
61The distributed data access queries all data contained in the query bounding box and transfers
62them to the calling cluster machine. The queried data is then assembled to form a single subset
63of the dataset, i.e., a regular 3D volume. This allows an application developer to inspect or export
64on the calling machine. It should be noted that the application developer is responsible to specify
65the query bounding box in a practicable and resource-aware way as an entire 3D regular volume dataset
66can be enormous (e.g., multiple terabyte) and can easily spill the available local memory.
67
68Distributed Data Editing
69========================
70The distributed data editing paradigm enables distributed editing of the voxel values of a
71regular 3D volume dataset. The application can implement an editing or computing task that can be
72applied to the each and every brick in the cluster environment. In combination with the data locality,
73which allows directing the computation to the machines that store the brick data, the editing paradigm
74has become a powerful tool that benefits from the data distribution and enables distributed and parallel
75data processing. The today's interfaces not only support the processing of the data in main memory but
76also the processing of the data in GPU memory.
77
78*/
79
80//----------------------------------------------------------------------
82 public nv::index::app::Index_connect
83{
84public:
86 :
87 Index_connect()
88 {
89 // INFO_LOG << "DEBUG: Distributed_heightfield_data() ctor";
90 }
91
93 {
94 // Note: Index_connect::~Index_connect() will be called after here.
95 // INFO_LOG << "DEBUG: ~Distributed_heightfield_data() dtor";
96 }
97
98 // launch application
99 mi::Sint32 launch();
100
101protected:
102 virtual bool evaluate_options(nv::index::app::String_dict& sdict) CPP11_OVERRIDE;
103 // override
105 mi::neuraylib::INetwork_configuration* network_configuration,
106 nv::index::app::String_dict& options) CPP11_OVERRIDE
107 {
108 check_success(network_configuration != 0);
109
110 check_success(options.is_defined("unittest"));
111 const bool is_unittest = nv::index::app::get_bool(options.get("unittest"));
112 if (is_unittest)
113 {
114 info_cout("NETWORK: disabled networking mode.", options);
115 network_configuration->set_mode(mi::neuraylib::INetwork_configuration::MODE_OFF);
116 return true;
117 }
118
119 return initialize_networking_as_default_udp(network_configuration, options);
120 }
121
122private:
123 mi::neuraylib::Tag add_synthetic_heightfield(
124 const mi::neuraylib::Tag& session_tag,
125 const std::string& synthetic_heightfield_type_str,
126 const std::string& technique_param_str,
127 const mi::math::Vector<mi::Uint32, 2>& heightfield_size,
128 mi::neuraylib::IDice_transaction* dice_transaction);
129 std::vector<mi::Uint32> evaluate_data_locality(
130 const nv::index::IDistributed_data_locality* data_locality,
131 const mi::math::Bbox<mi::Uint32, 2>& query_bound,
132 const mi::neuraylib::Tag& scene_element_tag,
133 const std::string& scene_element_type) const;
134 // Analyze the data in the heightfield, writing to the log
135 // \param[in] heightfield_tag Tag for the heightfield
136 // \param[in] session_tag Tag for the session
137 // \param[in] dice_transaction transaction
138 // \return average height
139 mi::Float32 analyze_heightfield_data(
140 const mi::neuraylib::Tag& heightfield_tag,
141 const mi::neuraylib::Tag& session_tag,
142 mi::neuraylib::IDice_transaction* dice_transaction) const;
143 // Edit the data in the heightfield
144 void edit_heightfield_data(
145 const mi::neuraylib::Tag& heightfield_tag,
146 const mi::neuraylib::Tag& session_tag,
147 mi::neuraylib::IDice_transaction* dice_transaction) const;
148 // Render a frame
149 // \param[in] output_fname output rendering image filename
150 // \return performance values
151 nv::index::IFrame_results* render_frame(const std::string& output_fname) const;
152
153 // This session tag
154 mi::neuraylib::Tag m_session_tag;
155 // NVIDIA IndeX cluster configuration
156 mi::base::Handle<nv::index::ICluster_configuration> m_cluster_configuration;
157 // Application layer image file canvas (a render target)
158 mi::base::Handle<nv::index::app::canvas_infrastructure::IIndex_image_file_canvas> m_image_file_canvas;
159 // Create_icons options
160 // std::string m_outfname;
161 bool m_is_unittest;
162};
163
164//----------------------------------------------------------------------
166{
167 mi::neuraylib::Tag heightfield_tag;
168 {
169 m_cluster_configuration =
170 get_index_interface()->
171 get_api_component<nv::index::ICluster_configuration>();
172 check_success(m_cluster_configuration.is_valid_interface());
173
174 // create image canvas in application_layer
175 m_image_file_canvas = create_image_file_canvas(get_application_layer_interface());
176 check_success(m_image_file_canvas.is_valid_interface());
177
178 {
179 // DiCE database access
180 mi::base::Handle<mi::neuraylib::IDice_transaction> dice_transaction(m_global_scope->create_transaction<mi::neuraylib::IDice_transaction>());
181 check_success(dice_transaction.is_valid_interface());
182 {
183 // Setup session information
184 m_session_tag = m_index_session->create_session(dice_transaction.get());
185 check_success(m_session_tag.is_valid());
186 mi::base::Handle<const nv::index::ISession> session(dice_transaction->access<nv::index::ISession>(m_session_tag));
187 check_success(session.is_valid_interface());
188
189 mi::base::Handle< nv::index::IScene > scene_edit(
190 dice_transaction->edit<nv::index::IScene>(session->get_scene()));
191 check_success(scene_edit.is_valid_interface());
192
193 // Configuration
194 {
195 mi::base::Handle<nv::index::IConfig_settings> edit_config_settings(dice_transaction->edit<nv::index::IConfig_settings>(session->get_config()));
196 check_success(edit_config_settings.is_valid_interface());
197
198 // All data should be uploaded immediately, even when it is not initially visible
199 edit_config_settings->set_force_data_upload(true);
200
201 // Set smaller subcube size in unit test mode (subcube size test)
202 if (m_is_unittest)
203 {
204 const mi::math::Vector<mi::Uint32, 3> subcube_size(30);
205 mi::math::Vector<mi::Float32, 3> minimal_volume_scaling;
206 edit_config_settings->get_minimal_volume_scaling(minimal_volume_scaling);
207 INFO_LOG << "minimal_volume_scaling: " << minimal_volume_scaling
208 << ", border: " << edit_config_settings->get_subcube_border_size()
209 << ", rotation support: "
210 << edit_config_settings->get_volume_rotation_supported();
211 edit_config_settings->set_subcube_configuration(
212 subcube_size,
213 edit_config_settings->get_continuous_volume_translation_supported(),
214 edit_config_settings->get_subcube_border_size(),
215 edit_config_settings->get_volume_rotation_supported(),
216 minimal_volume_scaling);
217 }
218 }
219
220 //----------------------------------------------------------------------
221 // Scene setup: add volume data, scene parameters
222 //----------------------------------------------------------------------
223
224 // Add a synthetic heightfield to the scene.
225 mi::math::Vector<mi::Uint32, 2> heightfield_size(1000, 1000);
226 if (m_is_unittest)
227 {
228 heightfield_size = mi::math::Vector<mi::Uint32, 2>(100, 100);
229 }
230 const std::string synthetic_heightfield_type_str = "i"; // synthetic_type
231 const std::string technique_param_str = "";
232 heightfield_tag = add_synthetic_heightfield(
233 m_session_tag, synthetic_heightfield_type_str, technique_param_str, heightfield_size,
234 dice_transaction.get());
235
236 check_success(heightfield_tag.is_valid());
237 INFO_LOG << "Created a synthetic regular heightfield: tag = " << heightfield_tag.id;
238
239 // Set up the scene
240 {
241 // Set region of interest to fit the volume in this example
242 mi::base::Handle<nv::index::IScene> scene(dice_transaction->edit<nv::index::IScene>(session->get_scene()));
243 check_success(scene.is_valid_interface());
244
245 const mi::math::Bbox<mi::Float32, 3> global_region_of_interest(
246 0.0f, 0.0f, 0.0f, 1000.f,1000.f,1000.f);
247 scene->set_clipped_bounding_box(global_region_of_interest);
248
249 // Create a camera
250 mi::base::Handle< nv::index::IPerspective_camera > cam(
251 scene_edit->create_camera<nv::index::IPerspective_camera>());
252 check_success(cam.is_valid_interface());
253 const mi::neuraylib::Tag camera_tag = dice_transaction->store(cam.get());
254 check_success(camera_tag.is_valid());
255
256 scene->set_camera(camera_tag);
257 }
258 const mi::math::Vector<mi::Uint32, 2> buffer_resolution(1024, 1024);
259 m_image_file_canvas->set_resolution(buffer_resolution);
260 }
261 dice_transaction->commit();
262 }
263
264 // Rendering
265 {
266 const std::string fname = ""; // no output file
267 render_frame(fname);
268 }
269
270 // Heightfield analysis
271
272 {
273 INFO_LOG << "Analyzing heightfield data before editing ...";
274 mi::base::Handle<mi::neuraylib::IDice_transaction> dice_transaction(
275 m_global_scope->create_transaction<mi::neuraylib::IDice_transaction>());
276 check_success(dice_transaction.is_valid_interface());
277 mi::Float32 value = analyze_heightfield_data(heightfield_tag, m_session_tag, dice_transaction.get());
278 if (m_is_unittest)
279 {
280 // sum(mod([0:k-1],255))*k/k^2 for 100 = 49.5
281 check_success(value == 49.5f);
282 }
283 else
284 {
285 // sum(mod([0:k-1],255))*k/k^2 for 1000 = 124.65
286 check_success(std::abs(value - 124.287f) < 0.0001f);
287 }
288 dice_transaction->commit();
289 }
290
291 // Height field edit
292 {
293 INFO_LOG << "Editing heightfield data...";
294 mi::base::Handle<mi::neuraylib::IDice_transaction> dice_transaction(
295 m_global_scope->create_transaction<mi::neuraylib::IDice_transaction>());
296 check_success(dice_transaction.is_valid_interface());
297 {
298 edit_heightfield_data(heightfield_tag, m_session_tag, dice_transaction.get());
299 }
300 dice_transaction->commit();
301 }
302
303 // Heightfield re-analysis
304 {
305 INFO_LOG << "Analyzing heightfield data after editing ...";
306 mi::base::Handle<mi::neuraylib::IDice_transaction> dice_transaction(
307 m_global_scope->create_transaction<mi::neuraylib::IDice_transaction>());
308 check_success(dice_transaction.is_valid_interface());
309 mi::Float32 value = analyze_heightfield_data(heightfield_tag, m_session_tag, dice_transaction.get());
310 if (m_is_unittest)
311 {
312 check_success(std::abs(value - 24.75f) < 0.0001f);
313 }
314 else
315 {
316 check_success(std::abs(value - 62.1435f) < 0.0001f);
317 }
318 dice_transaction->commit();
319 }
320 }
321 return 0;
322}
323
324//----------------------------------------------------------------------
325bool Distributed_heightfield_data::evaluate_options(nv::index::app::String_dict& sdict)
326{
327 const std::string com_name = sdict.get("command:", "<unknown_command>");
328 m_is_unittest = nv::index::app::get_bool(sdict.get("unittest", "false"));
329
330 if (m_is_unittest)
331 {
332 if (nv::index::app::get_bool(sdict.get("is_call_from_test", "false")))
333 {
334 sdict.insert("is_dump_comparison_image_when_failed", "0");
335 }
336 sdict.insert("dice::verbose", "2");
337 }
338
339 info_cout(std::string("running ") + com_name, sdict);
340
341 // print help and exit if -h
342 if (sdict.is_defined("h"))
343 {
344 std::cout
345 << "info: Usage: " << com_name <<" [option]\n"
346 << "Option: [-h]\n"
347 << " print this message\n"
348 << " [-dice::network::multicast_address address]\n"
349 << " set multicast address. (default: ["
350 << sdict.get("dice::network::multicast_address") + "])\n"
351 << " [-dice::network::cluster_interface]\n"
352 << " set cluster interface. (default: ["
353 << sdict.get("dice::network::cluster_interface") + "])\n"
354 << std::endl;
355 exit(1);
356 }
357 return true;
358}
359
360//----------------------------------------------------------------------
361mi::neuraylib::Tag Distributed_heightfield_data::add_synthetic_heightfield(
362 const mi::neuraylib::Tag& session_tag,
363 const std::string& synthetic_heightfield_type_str,
364 const std::string& technique_param_str,
365 const mi::math::Vector<mi::Uint32, 2>& heightfield_size,
366 mi::neuraylib::IDice_transaction* dice_transaction)
367{
368 check_success(session_tag.is_valid());
369 check_success(dice_transaction != 0);
370
371 // When a heightfield is created, we need to inform the IndeX library
372 // how to create the heightfield. This is a heightfield creation
373 // configuration. This configration is also a factory to generate
374 // a heightfield generator job.
375 nv::index::app::String_dict heightfield_opt;
376 heightfield_opt.insert("args::type", "heightfield");
377 heightfield_opt.insert("args::importer", "nv::index::plugin::legacy_importer.Synthetic_heightfield_generator");
378 heightfield_opt.insert("args::synthetic_type", synthetic_heightfield_type_str);
379 heightfield_opt.insert("args::parameter", technique_param_str);
380 std::stringstream sstr;
381 sstr << heightfield_size.x << " " << heightfield_size.y;
382 heightfield_opt.insert("args::size", sstr.str());
383 heightfield_opt.insert("args::range", "0.1 1000");
384 nv::index::IDistributed_data_import_callback* importer_callback =
385 get_importer_from_application_layer(
386 get_application_layer_interface(),
387 "nv::index::plugin::legacy_importer.Synthetic_heightfield_generator",
388 heightfield_opt);
389
390 mi::base::Handle<nv::index::ISession> session(dice_transaction->edit<nv::index::ISession>(session_tag));
391 check_success(session.is_valid_interface());
392
393 // Access (edit mode) the scene instance from the database.
394 mi::base::Handle<nv::index::IScene> scene_edit(dice_transaction->edit<nv::index::IScene>(session->get_scene()));
395 check_success(scene_edit.is_valid_interface());
396
397 const std::string heightfield_name = "distributed_heightfield_data";
398 const mi::math::Vector<mi::Float32, 3> scale(1.0f, 1.0f, 1.0f);
399 const mi::Float32 rotate_k = 0.0f;
400 const mi::math::Vector<mi::Float32, 3> translate(1.0f, 1.0f, 1.0f);
401
402 // use default range
403 const mi::math::Vector<mi::Float32, 2> heightfield_elevation_range(0.0f, 2000.0f);
404 mi::base::Handle<nv::index::IRegular_heightfield> heightfield_scene_element(
405 scene_edit->create_regular_heightfield(
406 scale, rotate_k, translate,
407 heightfield_size,
408 heightfield_elevation_range,
409 importer_callback,
410 dice_transaction));
411 check_success(heightfield_scene_element.is_valid_interface());
412
413 // Set the name of the heightfield
414 heightfield_scene_element->set_name(heightfield_name.c_str());
415 const mi::neuraylib::Tag heightfield_tag = dice_transaction->store_for_reference_counting(heightfield_scene_element.get());
416 check_success(heightfield_tag.is_valid());
417 {
418 // create static group node for large data and append the volume
419 mi::base::Handle<nv::index::IStatic_scene_group> static_group_node(
420 scene_edit->create_scene_group<nv::index::IStatic_scene_group>());
421 check_success(static_group_node.is_valid_interface());
422
423 // Added a light and a material to the static group node
424 mi::base::Handle<nv::index::IDirectional_headlight> headlight(scene_edit->create_attribute<nv::index::IDirectional_headlight>());
425 check_success(headlight.is_valid_interface());
426
427 const mi::math::Color_struct color_intensity = { 1.0f, 1.0f, 1.0f, 1.0f, };
428 headlight->set_intensity(color_intensity);
429 headlight->set_direction(mi::math::Vector<mi::Float32, 3>(1.0f, -1.0f, -1.0f));
430 const mi::neuraylib::Tag headlight_tag = dice_transaction->store_for_reference_counting(headlight.get());
431
432 check_success(headlight_tag.is_valid());
433 static_group_node->append(headlight_tag, dice_transaction);
434
435 // Material for the heightfield
436 mi::base::Handle<nv::index::IPhong_gl> phong_1(scene_edit->create_attribute<nv::index::IPhong_gl>());
437 check_success(phong_1.is_valid_interface());
438
439 const mi::neuraylib::Tag phong_1_tag = dice_transaction->store_for_reference_counting(phong_1.get());
440 check_success(phong_1_tag.is_valid());
441 static_group_node->append(phong_1_tag, dice_transaction);
442
443 // append heightfield to the scene
444 static_group_node->append(heightfield_tag, dice_transaction);
445 const mi::neuraylib::Tag static_group_node_tag = dice_transaction->store_for_reference_counting(static_group_node.get());
446 check_success(static_group_node_tag.is_valid());
447
448 // Add the new volume to the hierachical scene description
449 scene_edit->append(static_group_node_tag, dice_transaction);
450 }
451
452 DEBUG_LOG << "Adding the following synthetic heightfield to the scene: " << heightfield_name
453 << ", size: " << heightfield_size
454 << ", scale: " << scale
455 << ", rotate_k: " << rotate_k
456 << ", translate: " << translate
457 << ", (value) range: " << heightfield_elevation_range;
458
459 return heightfield_tag;
460}
461
462//----------------------------------------------------------------------
463std::vector<mi::Uint32> Distributed_heightfield_data::evaluate_data_locality(
464 const nv::index::IDistributed_data_locality* data_locality,
465 const mi::math::Bbox<mi::Uint32, 2>& query_bound,
466 const mi::neuraylib::Tag& scene_element_tag,
467 const std::string& scene_element_type) const
468{
469 // Determine all host in the cluster that store part of the distributed data (here: regular volume).
470 std::vector<mi::Uint32> cluster_host_ids;
471 std::ostringstream hosts;
472 std::ostringstream locality_report;
473 for (mi::Uint32 i=0; i < data_locality->get_nb_cluster_nodes(); ++i)
474 {
475 // A host id=0 indicates that an issue occurred, e.g., no data has been loaded or no distribution scheme has been set up.
476 check_success(data_locality->get_cluster_node(i) != 0);
477
478 const mi::Uint32 host_id = data_locality->get_cluster_node(i);
479 cluster_host_ids.push_back(host_id);
480
481 // The remaining part is just for reporting/logging.
482 hosts << host_id << " ";
483
484 const mi::Size nb_subregions = data_locality->get_nb_bounding_box(host_id);
485 locality_report << "Host " << host_id << " stores part of the distributed data in the following " << nb_subregions << " regions:\n";
486 for (mi::Uint32 j=0; j<nb_subregions; ++j)
487 {
488 const mi::math::Bbox<mi::Sint32, 3> bbox = data_locality->get_bounding_box(host_id, j);
489 locality_report << " " << (j+1) << ")\t" << bbox;
490 if (j < nb_subregions - 1)
491 {
492 locality_report << "\n";
493 }
494 }
495 }
496 INFO_LOG << "\n"
497 << "Data locality for a " << scene_element_type
498 << " with tag id " << scene_element_tag.id
499 << " and query region " << query_bound << "\n:"
500 << "The distributed data is located on the following hosts (ids): [ " << hosts.str() << "]." << "\n"
501 << locality_report.str()
502 << "\n";
503
504 return cluster_host_ids;
505}
506
507//----------------------------------------------------------------------
508mi::Float32 Distributed_heightfield_data::analyze_heightfield_data(
509 const mi::neuraylib::Tag& heightfield_tag,
510 const mi::neuraylib::Tag& session_tag,
511 mi::neuraylib::IDice_transaction* dice_transaction) const
512{
513 mi::base::Handle<const nv::index::ISession> the_session(dice_transaction->access<nv::index::ISession>(session_tag));
514 check_success(the_session.is_valid_interface());
515
516 mi::base::Handle<const nv::index::IRegular_heightfield> heightfield(
517 dice_transaction->access<nv::index::IRegular_heightfield>(heightfield_tag));
518 check_success(heightfield.is_valid_interface());
519
520 const mi::math::Bbox<mi::Float32, 3> heightfield_bbox = heightfield->get_IJK_bounding_box();
521
522 const mi::math::Bbox<mi::Uint32, 2> query_bbox(
523 static_cast<mi::Uint32>(heightfield_bbox.min.x), static_cast<mi::Uint32>(heightfield_bbox.min.y),
524 static_cast<mi::Uint32>(heightfield_bbox.max.x), static_cast<mi::Uint32>(heightfield_bbox.max.y));
525
526 // Access the distribution scheme
527 const mi::neuraylib::Tag dist_layout_tag = the_session->get_distribution_layout();
528 check_success(dist_layout_tag.is_valid());
529
530 mi::base::Handle<const nv::index::IData_distribution> distribution_layout(
531 dice_transaction->access<nv::index::IData_distribution>(dist_layout_tag));
532 check_success(distribution_layout.is_valid_interface());
533
534 // Find out on which hosts the data is located and print this information
535 mi::base::Handle<nv::index::Regular_heightfield_locality_query_mode> mode(
536 new nv::index::Regular_heightfield_locality_query_mode(heightfield_tag, query_bbox, false));
537 mi::base::Handle<nv::index::IDistributed_data_locality> data_locality(
538 distribution_layout->get_data_locality<nv::index::IRegular_heightfield>(mode.get(), dice_transaction));
539
540 // Determine all host in the cluster that store part of the distributed data (here: heightfield).
541 std::vector<mi::Uint32> cluster_host_ids = evaluate_data_locality(
542 data_locality.get(), query_bbox, heightfield_tag, heightfield->get_class_name());
543
544 // Create the access factory
545 const mi::neuraylib::Tag data_access_tag = the_session->get_data_access_factory();
546 check_success(data_access_tag.is_valid());
547
548 mi::base::Handle<const nv::index::IDistributed_data_access_factory> access_factory(
549 dice_transaction->access<nv::index::IDistributed_data_access_factory>(data_access_tag));
550 check_success(access_factory.is_valid_interface());
551
552 mi::base::Handle<nv::index::IRegular_heightfield_data_access> data_access(
553 access_factory->create_regular_heightfield_data_access(heightfield_tag));
554 check_success(data_access.is_valid_interface());
555
556 // Now retrieve the data
557 data_access->access(query_bbox, dice_transaction);
558
559 const mi::math::Bbox<mi::Uint32, 2> effective_bbox = data_access->get_patch_bounding_box();
560 INFO_LOG << effective_bbox;
561 INFO_LOG << query_bbox;
562
563 // Average the contents of the trace
564 const mi::Float32* height_data = data_access->get_elevation_values();
565
566 mi::Sint64 count = effective_bbox.volume();
567 mi::Float32 height_sum = 0;
568 mi::Float32 height_min = std::numeric_limits<mi::Float32>::max();
569 mi::Float32 height_max = std::numeric_limits<mi::Float32>::min();
570
571 for (mi::Sint64 k = 0; k < count; ++k)
572 {
573 mi::Float32 height = height_data[k];
574 height_sum += height;
575
576 if (height > height_max)
577 {
578 height_max = height;
579 }
580 if (height < height_min)
581 {
582 height_min = height;
583 }
584 }
585
586 mi::Float32 avg = static_cast<mi::Float32>(height_sum) / static_cast<mi::Float32>(count);
587 INFO_LOG << "Heightfield height: "
588 << "count=" << count
589 << ", min=" << height_min
590 << ", max=" << height_max
591 << ", avg=" << avg;
592
593 return avg;
594}
595
596//----------------------------------------------------------------------
597void Distributed_heightfield_data::edit_heightfield_data(
598 const mi::neuraylib::Tag& heightfield_tag,
599 const mi::neuraylib::Tag& session_tag,
600 mi::neuraylib::IDice_transaction* dice_transaction) const
601{
602 mi::base::Handle<const nv::index::ISession> the_session(dice_transaction->access<nv::index::ISession>(session_tag));
603 check_success(the_session.is_valid_interface());
604
605 mi::base::Handle<const nv::index::IRegular_heightfield> heightfield(dice_transaction->access<nv::index::IRegular_heightfield>(heightfield_tag));
606 check_success(heightfield.is_valid_interface());
607
608 const mi::math::Bbox<mi::Float32, 3> heightfield_bbox = heightfield->get_IJK_bounding_box();
609
610 const mi::math::Bbox<mi::Uint32, 2> query_bbox(
611 static_cast<mi::Uint32>(heightfield_bbox.min.x), static_cast<mi::Uint32>(heightfield_bbox.min.y),
612 static_cast<mi::Uint32>(heightfield_bbox.max.x), static_cast<mi::Uint32>(heightfield_bbox.max.y));
613
614 // Access the data distribution scheme that exposes the locality of the distribution data (here: heightfield).
615 const mi::neuraylib::Tag dist_layout_tag = the_session->get_distribution_layout();
616 check_success(dist_layout_tag.is_valid());
617 mi::base::Handle<const nv::index::IData_distribution> distribution_layout(dice_transaction->access<nv::index::IData_distribution>(dist_layout_tag));
618 check_success(distribution_layout.is_valid_interface());
619
620 // Distribution layout
621 mi::base::Handle<nv::index::Regular_heightfield_locality_query_mode> mode(
622 new nv::index::Regular_heightfield_locality_query_mode(heightfield_tag, query_bbox, false));
623 mi::base::Handle<nv::index::IDistributed_data_locality> data_locality(
624 distribution_layout->get_data_locality<nv::index::IRegular_heightfield>(mode.get(), dice_transaction));
625
626 // Determine all host in the cluster that store part of the distributed data (here: heightfield).
627 std::vector<mi::Uint32> cluster_host_ids = evaluate_data_locality(
628 data_locality.get(),
629 query_bbox,
630 heightfield_tag,
631 heightfield->get_class_name());
632
633 // Set up distributed compute algorithm and start the job.
634 const bool do_scaling = true;
635 const mi::Float32 scale_factor = 0.5f;
636 INFO_LOG << "-----------------Scaling the height field's elevation values using the following scale factor: " << scale_factor << "... ";
637
638 std::vector<mi::math::Vector_struct<mi::Float32, 2> > region;
639 region.push_back(mi::math::Vector<mi::Float32, 2>(heightfield_bbox.min.x, heightfield_bbox.min.y));
640 region.push_back(mi::math::Vector<mi::Float32, 2>(heightfield_bbox.min.x, heightfield_bbox.max.y));
641 region.push_back(mi::math::Vector<mi::Float32, 2>(heightfield_bbox.max.x, heightfield_bbox.max.y));
642 region.push_back(mi::math::Vector<mi::Float32, 2>(heightfield_bbox.max.x, heightfield_bbox.min.y));
643
644
645 // Access an application layer component that provides some sample jobs such as the heightfield elevation editing job:
646 mi::base::Handle<nv::index::app::data_analysis_and_processing::IData_analysis_and_processing> processing(
647 get_application_layer_interface()->get_api_component<nv::index::app::data_analysis_and_processing::IData_analysis_and_processing>());
648 check_success(processing.is_valid_interface());
649
650 // Create a distributed data job and schedule to run again the height field data. For more details on how to implement the such a job,
651 // please review the provided source that was shipped with the application layer component 'nv::index::app::data_analysis_and_processing::IData_analysis_and_processing'.
652 mi::base::Handle<nv::index::IDistributed_data_job> distributed_job(
653 processing->get_sample_tool_set()->create_heightfield_elevation_editing(
654 heightfield_tag, do_scaling, scale_factor, &region.at(0), 4));
655 check_success(distributed_job.is_valid_interface());
656
657 distribution_layout->create_scheduler()->execute(distributed_job.get(), dice_transaction);
658}
659
660//----------------------------------------------------------------------
661nv::index::IFrame_results* Distributed_heightfield_data::render_frame(const std::string& output_fname) const
662{
663 check_success(m_index_rendering.is_valid_interface());
664
665 // set output filename, empty string is valid
666 m_image_file_canvas->set_rgba_file_name(output_fname.c_str());
667
668 check_success(m_session_tag.is_valid());
669
670 mi::base::Handle<mi::neuraylib::IDice_transaction> dice_transaction(
671 m_global_scope->create_transaction<mi::neuraylib::IDice_transaction>());
672 check_success(dice_transaction.is_valid_interface());
673
674 m_index_session->update(m_session_tag, dice_transaction.get());
675
676 mi::base::Handle<nv::index::IFrame_results> frame_results(
677 m_index_rendering->render(
678 m_session_tag,
679 m_image_file_canvas.get(),
680 dice_transaction.get()));
681 check_success(frame_results.is_valid_interface());
682
683 dice_transaction->commit();
684
685 frame_results->retain();
686 return frame_results.get();
687}
688
689//----------------------------------------------------------------------
690// The API example illustrates the use of the general distributed computing concepts
691// data locality, access and edit applied to a sysnthetic heightfield dataset.
692//
693int main(int argc, const char* argv[])
694{
695 nv::index::app::String_dict sdict;
696 sdict.insert("dice::verbose", "3"); // log level
697 sdict.insert("unittest", "0"); // default mode
698 sdict.insert("dice::network::multicast_address", "224.1.3.2"); // default multicast address
699 sdict.insert("dice::network::cluster_interface", ""); // default cluster interface address
700 sdict.insert("is_dump_comparison_image_when_failed", "1"); // default: dump images when failed.
701 sdict.insert("is_call_from_test", "0"); // default: not call from make check.
702
703 // Load IndeX library via Index_connect
704 sdict.insert("dice::network::mode", "OFF");
705
706 // index setting
707 sdict.insert("index::config::set_monitor_performance_values", "true");
708 sdict.insert("index::service", "rendering_and_compositing");
709 sdict.insert("index::cuda_debug_checks", "false");
710
711 // application_layer component loading
712 sdict.insert("index::app::components::application_layer::component_name_list",
713 "canvas_infrastructure image io data_analysis_and_processing");
714 sdict.insert("index::app::plugins::base_importer::enabled", "true");
715 sdict.insert("index::app::plugins::legacy_importer::enabled", "true");
716
717 // Initialize application
718 Distributed_heightfield_data distributed_heightfield_data;
719 distributed_heightfield_data.initialize(argc, argv, sdict);
720 check_success(distributed_heightfield_data.is_initialized());
721
722 // launch the application. creating the scene and rendering.
723 const mi::Sint32 exit_code = distributed_heightfield_data.launch();
724 INFO_LOG << "Shutting down ...";
725
726 return exit_code;
727}
virtual bool initialize_networking(mi::neuraylib::INetwork_configuration *network_configuration, nv::index::app::String_dict &options) CPP11_OVERRIDE
virtual bool evaluate_options(nv::index::app::String_dict &sdict) CPP11_OVERRIDE
int main(int argc, const char *argv[])
#define check_success(expr)