1Open VKL API 2============ 3 4To access the Open VKL API you first need to include the Open VKL header. For 5C99 or C++: 6 7 #include <openvkl/openvkl.h> 8 9For the Intel® Implicit SPMD Program Compiler (Intel® ISPC): 10 11 #include <openvkl/openvkl.isph> 12 13This documentation will discuss the C99/C++ API. The ISPC version has the same 14functionality and flavor. Looking at the headers, the `vklTutorialISPC` 15example, and this documentation should be enough to figure it out. 16 17Device initialization and shutdown 18---------------------------------- 19 20To use the API, one of the implemented backends must be loaded. Currently the 21only one that exists is the CPU device. To load the module that implements the 22CPU device: 23 24 vklLoadModule("cpu_device"); 25 26The device then needs to be instantiated: 27 28 VKLDevice device = vklNewDevice("cpu"); 29 30By default, the CPU device selects the maximum supported SIMD width (and 31associated ISA) for the system. Optionally, a specific width may be requested 32using the `cpu_4`, `cpu_8`, or `cpu_16` device names. Note that the system 33must support the given width (SSE4.1 for 4-wide, AVX for 8-wide, and AVX512 for 3416-wide). 35 36Once a device is created, you can call 37 38 void vklDeviceSetInt(VKLDevice, const char *name, int val); 39 void vklDeviceSetString(VKLDevice, const char *name, const char *val); 40 41to set parameters on the device. The following parameters are understood by all 42devices: 43 44 ------ -------------- -------------------------------------------------------- 45 Type Name Description 46 ------ -------------- -------------------------------------------------------- 47 int logLevel logging level; valid values are `VKL_LOG_DEBUG`, 48 `VKL_LOG_INFO`, `VKL_LOG_WARNING`, `VKL_LOG_ERROR` and 49 `VKL_LOG_NONE` 50 51 string logOutput convenience for setting where log messages go; valid 52 values are `cout`, `cerr` and `none` 53 54 string errorOutput convenience for setting where error messages go; valid 55 values are `cout`, `cerr` and `none` 56 57 int numThreads number of threads which Open VKL can use 58 59 int flushDenormals sets the `Flush to Zero` and `Denormals are Zero` mode 60 of the MXCSR control and status register (default: 1); 61 see Performance Recommendations section for details 62 ------ -------------- -------------------------------------------------------- 63 : Parameters shared by all devices. 64 65Once parameters are set, the device must be committed with 66 67 vklCommitDevice(device); 68 69The newly committed device is then ready to use. Users may change parameters on 70a device after initialization. In this case the device would need to be 71re-committed. 72 73All Open VKL objects are associated with a device. A device handle must be 74explicitly provided when creating volume and data objects, via `vklNewVolume()` 75and `vklNewData()` respectively. Other object types are automatically associated 76with a device via transitive dependency on a volume. 77 78Open VKL provides vector-wide versions for several APIs. To determine the native 79vector width for a given device, call: 80 81 int width = vklGetNativeSIMDWidth(VKLDevice device); 82 83When the application is finished with an Open VKL device or shutting down, 84release the device via: 85 86 vklReleaseDevice(VKLDevice device); 87 88### Environment variables 89 90The generic device parameters can be overridden via environment variables for 91easy changes to Open VKL’s behavior without needing to change the application 92(variables are prefixed by convention with "`OPENVKL_`"): 93 94 ----------------------- ------------------------------------------------------ 95 Variable Description 96 ----------------------- ------------------------------------------------------ 97 OPENVKL_LOG_LEVEL logging level; valid values are `debug`, `info`, 98 `warning`, `error` and `none` 99 100 OPENVKL_LOG_OUTPUT convenience for setting where log messages go; valid 101 values are `cout`, `cerr` and `none` 102 103 OPENVKL_ERROR_OUTPUT convenience for setting where error messages go; valid 104 values are `cout`, `cerr` and `none` 105 106 OPENVKL_THREADS number of threads which Open VKL can use 107 108 OPENVKL_FLUSH_DENORMALS sets the `Flush to Zero` and `Denormals are Zero` mode 109 of the MXCSR control and status register (default: 1); 110 see Performance Recommendations section for details 111 ----------------------- ------------------------------------------------------ 112 : Environment variables understood by all devices. 113 114Note that these environment variables take precedence over values set through 115the `vklDeviceSet*()` functions. 116 117Additionally, the CPU device's default SIMD width can be overriden at run time 118with the `OPENVKL_CPU_DEVICE_DEFAULT_WIDTH` environment variable. Legal values 119are 4, 8, or 16. This setting is only applicable when the generic `cpu` device 120is instantiated; if a specific width is requested via the `cpu_[4,8,16]` device 121names then the environment variable is ignored. 122 123### Error handling and log messages 124 125The following errors are currently used by Open VKL: 126 127 ---------------------- ------------------------------------------------------- 128 Name Description 129 ---------------------- ------------------------------------------------------- 130 VKL_NO_ERROR no error occurred 131 132 VKL_UNKNOWN_ERROR an unknown error occurred 133 134 VKL_INVALID_ARGUMENT an invalid argument was specified 135 136 VKL_INVALID_OPERATION the operation is not allowed for the specified object 137 138 VKL_OUT_OF_MEMORY there is not enough memory to execute the command 139 140 VKL_UNSUPPORTED_CPU the CPU is not supported (minimum ISA is SSE4.1) 141 ---------------------- ------------------------------------------------------- 142 : Possible error codes, i.e., valid named constants of type `VKLError`. 143 144These error codes are either directly returned by some API functions, or are 145recorded to be later queried by the application via 146 147 VKLError vklDeviceGetLastErrorCode(VKLDevice); 148 149A more descriptive error message can be queried by calling 150 151 const char* vklDeviceGetLastErrorMsg(VKLDevice); 152 153Alternatively, the application can also register a callback function of type 154 155 typedef void (*VKLErrorCallback)(void *, VKLError, const char* message); 156 157via 158 159 void vklDeviceSetErrorCallback(VKLDevice, VKLErrorFunc, void *); 160 161to get notified when errors occur. Applications may be interested in messages 162which Open VKL emits, whether for debugging or logging events. Applications can 163register a callback function of type 164 165 typedef void (*VKLLogCallback)(void *, const char* message); 166 167via 168 169 void vklDeviceSetLogCallback(VKLDevice, VKLLogCallback, void *); 170 171which Open VKL will use to emit log messages. Applications can clear either 172callback by passing `nullptr` instead of an actual function pointer. By default, 173Open VKL uses `cout` and `cerr` to emit log and error messages, respectively. 174The last parameter to `vklDeviceSetErrorCallback` and `vklDeviceSetLogCallback` 175is a user data pointer. Open VKL passes this pointer to the callback functions as 176the first parameter. 177Note that in addition to setting the above callbacks, this behavior can be 178changed via the device parameters and environment variables described 179previously. 180 181Basic data types 182---------------- 183 184Open VKL defines 3-component vectors of integer and vector types: 185 186 typedef struct 187 { 188 int x, y, z; 189 } vkl_vec3i; 190 191 typedef struct 192 { 193 float x, y, z; 194 } vkl_vec3f; 195 196Vector versions of these are also defined in structure-of-array format for 4, 8, 197and 16 wide types. 198 199 typedef struct 200 { 201 float x[WIDTH]; 202 float y[WIDTH]; 203 float z[WIDTH]; 204 } vkl_vvec3f##WIDTH; 205 206 typedef struct 207 { 208 float lower[WIDTH], upper[WIDTH]; 209 } vkl_vrange1f##WIDTH; 210 2111-D range and 3-D ranges are defined as ranges and boxes, with no vector 212versions: 213 214 typedef struct 215 { 216 float lower, upper; 217 } vkl_range1f; 218 219 typedef struct 220 { 221 vkl_vec3f lower, upper; 222 } vkl_box3f; 223 224Object model 225------------ 226 227Objects in Open VKL are exposed to the APIs as handles with internal reference 228counting for lifetime determination. Objects are created with particular type's 229`vklNew...` API entry point. For example, `vklNewData` and `vklNewVolume`. 230 231In general, modifiable parameters to objects are modified using `vklSet...` 232functions based on the type of the parameter being set. The parameter name is 233passed as a string. Below are all variants of `vklSet...`. 234 235 void vklSetBool(VKLObject object, const char *name, int b); 236 void vklSetFloat(VKLObject object, const char *name, float x); 237 void vklSetVec3f(VKLObject object, const char *name, float x, float y, float z); 238 void vklSetInt(VKLObject object, const char *name, int x); 239 void vklSetVec3i(VKLObject object, const char *name, int x, int y, int z); 240 void vklSetData(VKLObject object, const char *name, VKLData data); 241 void vklSetString(VKLObject object, const char *name, const char *s); 242 void vklSetVoidPtr(VKLObject object, const char *name, void *v); 243 244After parameters have been set, `vklCommit` must be called on the object to make 245them take effect. 246 247Open VKL uses reference counting to manage the lifetime of all objects. 248Therefore one cannot explicitly "delete" any object. Instead, one can indicate 249the application does not need or will not access the given object anymore by 250calling 251 252 void vklRelease(VKLObject); 253 254This decreases the object's reference count. If the count reaches `0` the 255object will automatically be deleted. 256 257Managed data 258------------ 259 260Large data is passed to Open VKL via a `VKLData` handle created with 261`vklNewData`: 262 263 VKLData vklNewData(VKLDevice device, 264 size_t numItems, 265 VKLDataType dataType, 266 const void *source, 267 VKLDataCreationFlags dataCreationFlags, 268 size_t byteStride); 269 270Data objects can be created as Open VKL owned (`dataCreationFlags = 271VKL_DATA_DEFAULT`), in which the library will make a copy of the data for its 272use, or shared (`dataCreationFlags = VKL_DATA_SHARED_BUFFER`), which will try 273to use the passed pointer for usage. The library is allowed to copy data when 274a volume is committed. 275 276The distance between consecutive elements in `source` is given in bytes with 277`byteStride`. If the provided `byteStride` is zero, then it will be determined 278automatically as `sizeof(type)`. Open VKL owned data will be compacted into a 279naturally-strided array on copy, regardless of the original `byteStride`. 280 281As with other object types, when data objects are no longer needed they should 282be released via `vklRelease`. 283 284The enum type `VKLDataType` describes the different element types that can be 285represented in Open VKL. The types accepted vary per volume; see the volume 286section for specifics. Valid constants are listed in the table below. 287 288 -------------------------- --------------------------------------------------- 289 Type/Name Description 290 -------------------------- --------------------------------------------------- 291 VKL_DEVICE API device object reference 292 293 VKL_DATA data reference 294 295 VKL_OBJECT generic object reference 296 297 VKL_VOLUME volume object reference 298 299 VKL_STRING C-style zero-terminated character string 300 301 VKL_CHAR, VKL_VEC[234]C 8\ bit signed character scalar and [234]-element 302 vector 303 304 VKL_UCHAR, VKL_VEC[234]UC 8\ bit unsigned character scalar and [234]-element 305 vector 306 307 VKL_SHORT, VKL_VEC[234]S 16\ bit unsigned integer scalar and [234]-element 308 vector 309 310 VKL_USHORT, VKL_VEC[234]US 16\ bit unsigned integer scalar and [234]-element 311 vector 312 313 VKL_INT, VKL_VEC[234]I 32\ bit signed integer scalar and [234]-element 314 vector 315 316 VKL_UINT, VKL_VEC[234]UI 32\ bit unsigned integer scalar and [234]-element 317 vector 318 319 VKL_LONG, VKL_VEC[234]L 64\ bit signed integer scalar and [234]-element 320 vector 321 322 VKL_ULONG, VKL_VEC[234]UL 64\ bit unsigned integer scalar and [234]-element 323 vector 324 325 VKL_HALF, VKL_VEC[234]H 16\ bit half precision floating-point scalar and 326 [234]-element vector (IEEE 754 `binary16`) 327 328 VKL_FLOAT, VKL_VEC[234]F 32\ bit single precision floating-point scalar and 329 [234]-element vector 330 331 VKL_DOUBLE, VKL_VEC[234]D 64\ bit double precision floating-point scalar and 332 [234]-element vector 333 334 VKL_BOX[1234]I 32\ bit integer box (lower + upper bounds) 335 336 VKL_BOX[1234]F 32\ bit single precision floating-point box (lower 337 + upper bounds) 338 339 VKL_LINEAR[23]F 32\ bit single precision floating-point linear 340 transform ([23] vectors) 341 342 VKL_AFFINE[23]F 32\ bit single precision floating-point affine 343 transform (linear transform plus translation) 344 345 VKL_VOID_PTR raw memory address 346 -------------------------- --------------------------------------------------- 347 : Valid named constants for `VKLDataType`. 348 349Observers 350--------- 351 352Volumes and samplers in Open VKL may provide observers to communicate data back 353to the application. Observers may be created with 354 355 VKLObserver vklNewSamplerObserver(VKLSampler sampler, 356 const char *type); 357 358 VKLObserver vklNewVolumeObserver(VKLVolume volume, 359 const char *type); 360 361The object passed to `vklNew*Observer` must already be committed. Valid 362observer type strings are defined by volume implementations (see section 363'Volume types' below). 364 365`vklNew*Observer` returns `NULL` on failure. 366 367To access the underlying data, an observer must first be mapped using 368 369 const void * vklMapObserver(VKLObserver observer); 370 371If this fails, the function returns `NULL`. `vklMapObserver` may fail on 372observers that are already mapped. 373On success, the application may query the underlying type, element size in 374bytes, and the number of elements in the buffer using 375 376 VKLDataType vklGetObserverElementType(VKLObserver observer); 377 size_t vklGetObserverElementSize(VKLObserver observer); 378 size_t vklGetObserverNumElements(VKLObserver observer); 379 380On failure, these functions return `VKL_UNKNOWN` and `0`, respectively. 381Possible data types are defined by the volume that provides the observer , as 382are the semantics of the observation. See section 'Volume types' for details. 383 384The pointer returned by `vklMapObserver` may be cast to the type corresponding 385to the value returned by `vklGetObserverElementType` to access the observation. 386For example, if `vklGetObserverElementType` returns `VKL_FLOAT`, then 387the pointer returned by `vklMapObserver` may be cast to `const float *` to access 388up to `vklGetObserverNumElements` consecutive values of type `float`. 389 390Once the application has finished processing the observation, it should unmap 391the observer using 392 393 void vklUnmapObserver(VKLObserver observer); 394 395so that the observer may be mapped again. 396 397When an observer is no longer needed, it should be released using `vklRelease`. 398 399The observer API is not thread safe, and these functions should not 400be called concurrently on the same object. 401 402 403Volume types 404------------ 405 406Open VKL currently supports structured volumes on regular and spherical grids; 407unstructured volumes with tetrahedral, wedge, pyramid, and hexaderal primitive 408types; adaptive mesh refinement (AMR) volumes; sparse VDB volumes; and particle 409volumes. Volumes are created with `vklNewVolume` with a device and appropriate 410type string: 411 412 VKLVolume vklNewVolume(VKLDevice device, const char *type); 413 414In addition to the usual `vklSet...()` and `vklCommit()` APIs, the volume 415bounding box can be queried: 416 417 vkl_box3f vklGetBoundingBox(VKLVolume volume); 418 419The number of attributes in a volume can also be queried: 420 421 unsigned int vklGetNumAttributes(VKLVolume volume); 422 423Finally, the value range of the volume for a given attribute can be queried: 424 425 vkl_range1f vklGetValueRange(VKLVolume volume, unsigned int attributeIndex); 426 427### Structured Volumes 428 429Structured volumes only need to store the values of the samples, because their 430addresses in memory can be easily computed from a 3D position. The dimensions 431for all structured volume types are in units of vertices, not cells. For 432example, a volume with dimensions $(x, y, z)$ will have $(x-1, y-1, z-1)$ cells 433in each dimension. Voxel data provided is assumed vertex-centered, so $x*y*z$ 434values must be provided. 435 436#### Structured Regular Volumes 437 438A common type of structured volumes are regular grids, which are 439created by passing a type string of `"structuredRegular"` to `vklNewVolume`. 440The parameters understood by structured regular volumes are summarized in the 441table below. 442 443 --------- -------------------------------- ----------------------------- --------------------------------------- 444 Type Name Default Description 445 --------- -------------------------------- ----------------------------- --------------------------------------- 446 vec3i dimensions number of voxels in each 447 dimension $(x, y, z)$ 448 449 VKLData data VKLData object(s) of voxel data, 450 VKLData[] supported types are: 451 452 `VKL_UCHAR` 453 454 `VKL_SHORT` 455 456 `VKL_USHORT` 457 458 `VKL_HALF` 459 460 `VKL_FLOAT` 461 462 `VKL_DOUBLE` 463 464 Multiple attributes are supported 465 through passing an array of VKLData 466 objects. 467 468 vec3f gridOrigin $(0, 0, 0)$ origin of the grid in object space 469 470 vec3f gridSpacing $(1, 1, 1)$ size of the grid cells in object space 471 472 uint32 temporalFormat `VKL_TEMPORAL_FORMAT_CONSTANT` The temporal format for this volume. 473 Use `VKLTemporalFormat` for named 474 constants. 475 Structured regular volumes support 476 `VKL_TEMPORAL_FORMAT_CONSTANT`, 477 `VKL_TEMPORAL_FORMAT_STRUCTURED`, and 478 `VKL_TEMPORAL_FORMAT_UNSTRUCTURED`. 479 480 int temporallyStructuredNumTimesteps For temporally structured variation, 481 number of timesteps per voxel. Only 482 valid if `temporalFormat` is 483 `VKL_TEMPORAL_FORMAT_STRUCTURED`. 484 485 uint32[] temporallyUnstructuredIndices For temporally unstructured variation, 486 uint64[] indices to `data` time series beginning 487 per voxel. 488 Only valid if `temporalFormat` is 489 `VKL_TEMPORAL_FORMAT_UNSTRUCTURED`. 490 491 float[] temporallyUnstructuredTimes For temporally unstructured variation, 492 time values corresponding to values in 493 `data`. 494 Only valid if `temporalFormat` is 495 `VKL_TEMPORAL_FORMAT_UNSTRUCTURED`. 496 497 float[] background `VKL_BACKGROUND_UNDEFINED` For each attribute, the value that is 498 returned when sampling an undefined 499 region outside the volume domain. 500 --------- -------------------------------- ----------------------------- --------------------------------------- 501 : Configuration parameters for structured regular (`"structuredRegular"`) volumes. 502 503Structured regular volumes support temporally structured and temporally 504unstructured temporal variation. See section 'Temporal Variation' for more 505detail. 506 507The following additional parameters can be set both on `"structuredRegular"` 508volumes and their sampler objects. Sampler object parameters default to volume 509parameters. 510 511 ------------ ---------------- ---------------------- --------------------------------------- 512 Type Name Default Description 513 ------------ ---------------- ---------------------- --------------------------------------- 514 int filter `VKL_FILTER_TRILINEAR` The filter used for reconstructing the 515 field. Use `VKLFilter` for named 516 constants. 517 518 int gradientFilter `filter` The filter used for reconstructing the 519 field during gradient computations. 520 Use `VKLFilter` for named constants. 521 ------------ ---------------- ---------------------- --------------------------------------- 522 : Configuration parameters for structured regular (`"structuredRegular"`) volumes and their sampler objects. 523 524 525##### Reconstruction filters 526 527Structured regular volumes support the filter types `VKL_FILTER_NEAREST`, 528`VKL_FILTER_TRILINEAR`, and `VKL_FILTER_TRICUBIC` for both `filter` and 529`gradientFilter`. 530 531Note that when `gradientFilter` is set to `VKL_FILTER_NEAREST`, gradients are 532always $(0, 0, 0)$. 533 534#### Structured Spherical Volumes 535 536Structured spherical volumes are also supported, which are created by passing a 537type string of `"structuredSpherical"` to `vklNewVolume`. The grid dimensions 538and parameters are defined in terms of radial distance ($r$), inclination angle 539($\theta$), and azimuthal angle ($\phi$), conforming with the ISO convention for 540spherical coordinate systems. The coordinate system and parameters understood by 541structured spherical volumes are summarized below. 542 543![Structured spherical volume coordinate system: radial distance ($r$), inclination angle ($\theta$), and azimuthal angle ($\phi$).][imgStructuredSphericalCoords] 544 545 --------- ----------------------- -------------------------- ----------------------------------- 546 Type Name Default Description 547 --------- ----------------------- -------------------------- ----------------------------------- 548 vec3i dimensions number of voxels in each 549 dimension $(r, \theta, \phi)$ 550 551 VKLData data VKLData object(s) of voxel data, 552 VKLData[] supported types are: 553 554 `VKL_UCHAR` 555 556 `VKL_SHORT` 557 558 `VKL_USHORT` 559 560 `VKL_HALF` 561 562 `VKL_FLOAT` 563 564 `VKL_DOUBLE` 565 566 Multiple attributes are supported 567 through passing an array of VKLData 568 objects. 569 570 vec3f gridOrigin $(0, 0, 0)$ origin of the grid in units of 571 $(r, \theta, \phi)$; angles in degrees 572 573 vec3f gridSpacing $(1, 1, 1)$ size of the grid cells in units of 574 $(r, \theta, \phi)$; angles in degrees 575 576 float[] background `VKL_BACKGROUND_UNDEFINED` For each attribute, the value that is 577 returned when sampling an undefined 578 region outside the volume domain. 579 --------- ----------------------- -------------------------- ----------------------------------- 580 : Configuration parameters for structured spherical (`"structuredSpherical"`) volumes. 581 582These grid parameters support flexible specification of spheres, hemispheres, 583spherical shells, spherical wedges, and so forth. The grid extents (computed as 584$[gridOrigin, gridOrigin + (dimensions - 1) * gridSpacing]$) however must be 585constrained such that: 586 587 * $r \geq 0$ 588 * $0 \leq \theta \leq 180$ 589 * $0 \leq \phi \leq 360$ 590 591The following additional parameters can be set both on `"structuredSpherical"` 592volumes and their sampler objects. Sampler object parameters default to volume 593parameters. 594 595 ------------ ---------------- ---------------------- --------------------------------------- 596 Type Name Default Description 597 ------------ ---------------- ---------------------- --------------------------------------- 598 int filter `VKL_FILTER_TRILINEAR` The filter used for reconstructing the 599 field. Use `VKLFilter` for named 600 constants. 601 602 int gradientFilter `filter` The filter used for reconstructing the 603 field during gradient computations. 604 Use `VKLFilter` for named constants. 605 ------------ ---------------- ---------------------- --------------------------------------- 606 : Configuration parameters for structured spherical (`"structuredSpherical"`) volumes and their sampler objects. 607 608 609### Adaptive Mesh Refinement (AMR) Volumes 610 611Open VKL currently supports block-structured (Berger-Colella) AMR volumes. 612Volumes are specified as a list of blocks, which exist at levels of refinement 613in potentially overlapping regions. Blocks exist in a tree structure, with 614coarser refinement level blocks containing finer blocks. The cell width is 615equal for all blocks at the same refinement level, though blocks at a coarser 616level have a larger cell width than finer levels. 617 618There can be any number of refinement levels and any number of blocks at any 619level of refinement. 620 621Blocks are defined by three parameters: their bounds, the refinement level in 622which they reside, and the scalar data contained within each block. 623 624Note that cell widths are defined _per refinement level_, not per block. 625 626AMR volumes are created by passing the type string `"amr"` to `vklNewVolume`, 627and have the following parameters: 628 629 -------------- --------------------- -------------------------- ----------------------------------- 630 Type Name Default Description 631 -------------- --------------------- -------------------------- ----------------------------------- 632 float[] cellWidth [data] array of each level's cell width 633 634 box3i[] block.bounds [data] array of each block's bounds (in voxels) 635 636 int[] block.level [data] array of each block's refinement level 637 638 VKLData[] block.data [data] array of each block's VKLData object 639 containing the actual scalar voxel data. 640 Currently only `VKL_FLOAT` data is supported. 641 642 vec3f gridOrigin $(0, 0, 0)$ origin of the grid in object space 643 644 vec3f gridSpacing $(1, 1, 1)$ size of the grid cells in object 645 space 646 647 float background `VKL_BACKGROUND_UNDEFINED` The value that is returned when sampling an 648 undefined region outside the volume domain. 649 -------------- --------------------- -------------------------- ----------------------------------- 650 : Configuration parameters for AMR (`"amr"`) volumes. 651 652Note that the `gridOrigin` and `gridSpacing` parameters act just like the 653structured volume equivalent, but they only modify the root (coarsest level) of 654refinement. 655 656The following additional parameters can be set both on `"amr"` 657volumes and their sampler objects. Sampler object parameters default to volume 658parameters. 659 660 -------------- ------------------ ----------------- ------------------------------------- 661 Type Name Default Description 662 -------------- ------------------ ----------------- ------------------------------------- 663 `VKLAMRMethod` method `VKL_AMR_CURRENT` `VKLAMRMethod` sampling method. 664 Supported methods are: 665 666 `VKL_AMR_CURRENT` 667 668 `VKL_AMR_FINEST` 669 670 `VKL_AMR_OCTANT` 671 -------------- ------------------ ----------------- ------------------------------------- 672 : Configuration parameters for AMR (`"AMR"`) volumes and their sampler objects. 673 674Open VKL's AMR implementation was designed to cover Berger-Colella [1] and 675Chombo [2] AMR data. The `method` parameter above determines the interpolation 676method used when sampling the volume. 677 678* `VKL_AMR_CURRENT` finds the finest refinement level at that cell and 679 interpolates through this "current" level 680* `VKL_AMR_FINEST` will interpolate at the closest existing cell in the 681 volume-wide finest refinement level regardless of the sample cell's level 682* `VKL_AMR_OCTANT` interpolates through all available refinement levels at that 683 cell. This method avoids discontinuities at refinement level boundaries at 684 the cost of performance 685 686Gradients are computed using finite differences, using the `method` defined on 687the sampler. 688 689Details and more information can be found in the publication for the 690implementation [3]. 691 6921. M. J. Berger, and P. Colella. "Local adaptive mesh refinement for 693 shock hydrodynamics." Journal of Computational Physics 82.1 (1989): 64-84. 694 DOI: 10.1016/0021-9991(89)90035-1 6952. M. Adams, P. Colella, D. T. Graves, J.N. Johnson, N.D. Keen, T. J. Ligocki. 696 D. F. Martin. P.W. McCorquodale, D. Modiano. P.O. Schwartz, T.D. Sternberg 697 and B. Van Straalen, Chombo Software Package for AMR Applications - Design 698 Document, Lawrence Berkeley National Laboratory Technical Report 699 LBNL-6616E. 7003. I. Wald, C. Brownlee, W. Usher, and A. Knoll. CPU volume rendering of 701 adaptive mesh refinement data. SIGGRAPH Asia 2017 Symposium on Visualization 702 on - SA ’17, 18(8), 1–8. DOI: 10.1145/3139295.3139305 703 704### Unstructured Volumes 705 706Unstructured volumes can have their topology and geometry freely defined. 707Geometry can be composed of tetrahedral, hexahedral, wedge or pyramid cell 708types. The data format used is compatible with VTK and consists of multiple 709arrays: vertex positions and values, vertex indices, cell start indices, cell 710types, and cell values. 711 712Sampled cell values can be specified either per-vertex (`vertex.data`) or 713per-cell (`cell.data`). If both arrays are set, `cell.data` takes precedence. 714 715Similar to a mesh, each cell is formed by a group of indices into the vertices. 716For each vertex, the corresponding (by array index) data value will be used for 717sampling when rendering, if specified. The index order for a tetrahedron is the 718same as `VTK_TETRA`: bottom triangle counterclockwise, then the top vertex. 719 720For hexahedral cells, each hexahedron is formed by a group of eight indices into 721the vertices and data values. Vertex ordering is the same as `VTK_HEXAHEDRON`: 722four bottom vertices counterclockwise, then top four counterclockwise. 723 724For wedge cells, each wedge is formed by a group of six indices into the 725vertices and data values. Vertex ordering is the same as `VTK_WEDGE`: three 726bottom vertices counterclockwise, then top three counterclockwise. 727 728For pyramid cells, each cell is formed by a group of five indices into the 729vertices and data values. Vertex ordering is the same as `VTK_PYRAMID`: four 730bottom vertices counterclockwise, then the top vertex. 731 732To maintain VTK data compatibility, the `index` array may be specified with cell 733sizes interleaved with vertex indices in the following format: $n, id_1, ..., 734id_n, m, id_1, ..., id_m$. This alternative `index` array layout can be enabled 735through the `indexPrefixed` flag (in which case, the `cell.type` parameter 736should be omitted). 737 738Gradients are computed using finite differences. 739 740Unstructured volumes are created by passing the type string `"unstructured"` to 741`vklNewVolume`, and have the following parameters: 742 743 ------------------- -------------------- ------------------------- --------------------------------------- 744 Type Name Default Description 745 ------------------- -------------------- ------------------------- --------------------------------------- 746 vec3f[] vertex.position [data] array of vertex positions 747 748 float[] vertex.data [data] array of vertex data values to 749 be sampled 750 751 uint32[] / uint64[] index [data] array of indices (into the 752 vertex array(s)) that form cells 753 754 bool indexPrefixed false indicates that the `index` array is 755 provided in a VTK-compatible format, 756 where the indices of each cell are 757 prefixed with the number of vertices 758 759 uint32[] / uint64[] cell.index [data] array of locations (into the 760 index array), specifying the first index 761 of each cell 762 763 float[] cell.data [data] array of cell data values to be 764 sampled 765 766 uint8[] cell.type [data] array of cell types 767 (VTK compatible). Supported types are: 768 769 `VKL_TETRAHEDRON` 770 771 `VKL_HEXAHEDRON` 772 773 `VKL_WEDGE` 774 775 `VKL_PYRAMID` 776 777 bool hexIterative false hexahedron 778 interpolation method, defaults to fast 779 non-iterative version which could have 780 rendering inaccuracies may appear 781 if hex is not parallelepiped 782 783 bool precomputedNormals false whether to accelerate by precomputing, 784 at a cost of 12 bytes/face 785 786 float background `VKL_BACKGROUND_UNDEFINED` The value that is returned when 787 sampling an undefined region outside 788 the volume domain. 789 ------------------- -------------------- ------------------------- --------------------------------------- 790 : Configuration parameters for unstructured (`"unstructured"`) volumes. 791 792### VDB Volumes 793 794VDB volumes implement a data structure that is very similar to the data structure 795outlined in Museth [1]. 796 797The data structure is a hierarchical regular grid at its core: Nodes are regular grids, 798and each grid cell may either store a constant value (this is called a tile), or 799child pointers. 800 801Nodes in VDB trees are wide: Nodes on the first level have a resolution of 32^3 voxels 802by default, on the next level 16^3, and on the leaf level 8^3 voxels. All nodes 803on a given level have the same resolution. This makes it easy to find the node 804containing a coordinate using shift operations (cp. [1]). 805 806VDB leaf nodes are implicit in Open VKL: they are stored as pointers to user-provided data. 807 808![Structure of `"vdb"` volumes in the default configuration][imgVdbStructure] 809 810VDB volumes interpret input data as constant cells (which are then potentially filtered). 811This is in contrast to `structuredRegular` volumes, which have a vertex-centered 812interpretation. 813 814The VDB implementation in Open VKL follows the following goals: 815 816 - Efficient data structure traversal on vector architectures. 817 818 - Enable the use of industry-standard .vdb files created through the OpenVDB library. 819 820 - Compatibility with OpenVDB on a leaf data level, so that .vdb files may be loaded 821 with minimal overhead. 822 823VDB volumes are created by passing the type string `"vdb"` to `vklNewVolume`, and have the 824following parameters: 825 826 ------------ ------------------------------------- ------------------------------ --------------------------------------- 827 Type Name Default Description 828 ------------ ------------------------------------- ------------------------------ --------------------------------------- 829 float[] indexToObject 1, 0, 0, An array of 12 values of type `float` 830 0, 1, 0, that define the transformation from 831 0, 0, 1, index space to object space. 832 0, 0, 0 In index space, the grid is an 833 axis-aligned regular grid, and leaf 834 voxels have size (1,1,1). 835 The first 9 values are interpreted 836 as a row-major linear transformation 837 matrix. The last 3 values are the 838 translation of the grid origin. 839 840 uint32[] node.format For each input node, the data format. 841 Currently supported are 842 `VKL_FORMAT_TILE` for tiles, 843 and `VKL_FORMAT_DENSE_ZYX` for 844 nodes that are dense regular grids. 845 846 uint32[] node.level For each input node, the level on 847 which this node exists. Tiles may exist 848 on levels [1, `VKL_VDB_NUM_LEVELS-1`], 849 all other nodes may only exist on level 850 `VKL_VDB_NUM_LEVELS-1`. 851 852 vec3i[] node.origin For each input node, the node origin 853 index. 854 855 VKLData[] node.data For each input node, the attribute 856 data. Single-attribute volumes may have 857 one array provided per node, while 858 multi-attribute volumes require an 859 array per attribute for each node. 860 Nodes with format `VKL_FORMAT_TILE` are 861 expected to have single-entry arrays 862 per attribute. Nodes with format 863 `VKL_FORMAT_DENSE_ZYX` are expected 864 to have arrays with 865 `vklVdbLevelNumVoxels(level[i])` 866 entries per attribute. `VKL_HALF` and 867 `VKL_FLOAT` data is currently 868 supported; all nodes for a given 869 attribute must be the same data type. 870 871 uint32[] node.temporalFormat `VKL_TEMPORAL_FORMAT_CONSTANT` The temporal format for this volume. 872 Use `VKLTemporalFormat` for named 873 constants. 874 VDB volumes support 875 `VKL_TEMPORAL_FORMAT_CONSTANT`, 876 `VKL_TEMPORAL_FORMAT_STRUCTURED`, and 877 `VKL_TEMPORAL_FORMAT_UNSTRUCTURED`. 878 879 int[] node.temporallyStructuredNumTimesteps For temporally structured variation, 880 number of timesteps per voxel. Only 881 valid if `temporalFormat` is 882 `VKL_TEMPORAL_FORMAT_STRUCTURED`. 883 884 VKLData[] node.temporallyUnstructuredIndices For temporally unstructured variation, 885 beginning per voxel. Supported data 886 types for each node are `VKL_UINT` 887 and `VKL_ULONG`. 888 Only valid if `temporalFormat` is 889 `VKL_TEMPORAL_FORMAT_UNSTRUCTURED`. 890 891 VKLData[] node.temporallyUnstructuredTimes For temporally unstructured variation, 892 time values corresponding to values in 893 `node.data`. For each node, the data 894 must be of type `VKL_FLOAT`. 895 Only valid if `temporalFormat` is 896 `VKL_TEMPORAL_FORMAT_UNSTRUCTURED`. 897 898 float[] background `VKL_BACKGROUND_UNDEFINED` For each attribute, the value that is 899 returned when sampling an undefined 900 region outside the volume domain. 901 ------------ ------------------------------------- ------------------------------ --------------------------------------- 902 : Configuration parameters for VDB (`"vdb"`) volumes. 903 904The level, origin, format, and data parameters must have the same size, and there must 905be at least one valid node or `commit()` will fail. 906 907VDB volumes support temporally structured and temporally unstructured temporal 908variation. See section 'Temporal Variation' for more detail. 909 910The following additional parameters can be set both on `vdb` volumes and their sampler 911objects (sampler object parameters default to volume parameters). 912 913 ------------ ---------------- ---------------------- --------------------------------------- 914 Type Name Default Description 915 ------------ ---------------- ---------------------- --------------------------------------- 916 int filter `VKL_FILTER_TRILINEAR` The filter used for reconstructing the 917 field. Use `VKLFilter` for named 918 constants. 919 920 int gradientFilter `filter` The filter used for reconstructing the 921 field during gradient computations. 922 Use `VKLFilter` for named constants. 923 924 int maxSamplingDepth `VKL_VDB_NUM_LEVELS`-1 Do not descend further than to this 925 depth during sampling. 926 ------------ ---------------- ---------------------- --------------------------------------- 927 : Configuration parameters for VDB (`"vdb"`) volumes and their sampler objects. 928 929VDB volume objects support the following observers: 930 931 -------------- ----------- ------------------------------------------------------------- 932 Name Buffer Type Description 933 -------------- ----------- ------------------------------------------------------------- 934 InnerNode float[] Return an array of bounding boxes, along with value ranges, 935 of inner nodes in the data structure. The bounding box is 936 given in object space. 937 For a volume with M attributes, the entries in this array 938 are (6+2*M)-tuples 939 `(minX, minY, minZ, maxX, maxY, maxZ, lower_0, upper_0, 940 lower_1, upper_1, ...)`. 941 This is in effect a low resolution representation of the 942 volume. 943 The InnerNode observer can be parametrized using 944 `int maxDepth` to control the depth at which inner nodes are 945 returned. Note that the observer will also return leaf nodes 946 or tiles at lower levels if they exist. 947 -------------- -------------------------------------------------------------------------- 948 : Observers supported by VDB (`"vdb"`) volumes. 949 950VDB sampler objects support the following observers: 951 952 -------------- ----------- ------------------------------------------------------------- 953 Name Buffer Type Description 954 -------------- ----------- ------------------------------------------------------------- 955 LeafNodeAccess uint32[] This observer returns an array with as many entries as 956 input nodes were passed. If the input node i was accessed 957 during traversal, then the ith entry in this array has a 958 nonzero value. 959 This can be used for on-demand loading of leaf nodes. 960 -------------- -------------------------------------------------------------------------- 961 : Observers supported by sampler objects created on VDB (`"vdb"`) volumes. 962 963#### Reconstruction filters 964 965VDB volumes support the filter types `VKL_FILTER_NEAREST`, `VKL_FILTER_TRILINEAR`, 966and `VKL_FILTER_TRICUBIC` for both `filter` and `gradientFilter`. 967 968Note that when `gradientFilter` is set to `VKL_FILTER_NEAREST`, gradients are 969always $(0, 0, 0)$. 970 971#### Major differences to OpenVDB 972 973 - Open VKL implements sampling in ISPC, and can exploit wide SIMD architectures. 974 975 - VDB volumes in Open VKL are read-only once committed, and designed for rendering only. 976 Authoring or manipulating datasets is not in the scope of this implementation. 977 978 - The only supported field types are `VKL_HALF` and `VKL_FLOAT` at this point. 979 Other field types may be supported in the future. Note that multi-attribute 980 volumes may be used to represent multi-component (e.g. vector) fields. 981 982 - The root level in Open VKL has a single node with resolution 64^3 (cp. [1]. OpenVDB 983 uses a hash map, instead). 984 985 - Open VKL supports four-level vdb volumes. The resolution of each level 986 can be configured at compile time using CMake variables. 987 * `VKL_VDB_LOG_RESOLUTION_0` sets the base 2 logarithm of the root level 988 resolution. This variable defaults to 6, which means that the root level 989 has a resolution of $(2^6)^3 = 64^3$. 990 * `VKL_VDB_LOG_RESOLUTION_1` and `VKL_VDB_LOG_RESOLUTION_2` default to 991 5 and 4, respectively. This matches the default Open VDB resolution for 992 inner levels. 993 * `VKL_VDB_LOG_RESOLUTION_3` set the base 2 logarithm of the leaf level 994 resolution, and defaults to 3. Therefore, leaf nodes have a resolution 995 of $8^3$ voxels. Again, this matches the Open VDB default. 996 The default settings lead to a domain resolution of $2^18^3=262144^3$ voxels. 997 998 999#### Loading OpenVDB .vdb files 1000 1001Files generated with OpenVDB can be loaded easily since Open VKL `vdb` volumes 1002implement the same leaf data layout. This means that OpenVDB leaf data pointers 1003can be passed to Open VKL using shared data buffers, avoiding copy operations. 1004 1005An example of this can be found in 1006`utility/vdb/include/openvkl/utility/vdb/OpenVdbGrid.h`, where the class 1007`OpenVdbFloatGrid` encapsulates the necessary operations. This class is also 1008accessible through the `vklExamples` application using the `-file` and `-field` 1009command line arguments. 1010 1011To use this example feature, compile Open VKL with `OpenVDB_ROOT` pointing to 1012the OpenVDB prefix. 1013 1014 10151. Museth, K. VDB: High-Resolution Sparse Volumes with Dynamic Topology. 1016 ACM Transactions on Graphics 32(3), 2013. DOI: 10.1145/2487228.2487235 1017 1018 1019### Particle Volumes 1020 1021Particle volumes consist of a set of points in space. Each point has a position, 1022a radius, and a weight typically associated with an attribute. A radial basis 1023function defines the contribution of that particle. Currently, we use the 1024Gaussian radial basis function, 1025 1026phi(P) = w * exp( -0.5 * ((P - p) / r)^2 ) 1027 1028where P is the particle position, p is the sample position, r is the radius and 1029w is the weight. 1030 1031At each sample, the scalar field value is then computed as the sum of each 1032radial basis function phi, for each particle that overlaps it. Gradients are 1033similarly computed, based on the summed analytical contributions of each 1034contributing particle. 1035 1036The Open VKL implementation is similar to direct evaluation of samples in Reda 1037et al.[2]. It uses an Embree-built BVH with a custom traversal, similar to the 1038method in [1]. 1039 1040Particle volumes are created by passing the type string `"particle"` to 1041`vklNewVolume`, and have the following parameters: 1042 1043 -------- -------------------------- -------- --------------------------------------- 1044 Type Name Default Description 1045 -------- -------------------------- -------- --------------------------------------- 1046 vec3f[] particle.position [data] array of particle positions 1047 1048 float[] particle.radius [data] array of particle radii 1049 1050 float[] particle.weight null [data] (optional) array of particle 1051 weights, specifying the height of the 1052 kernel. 1053 1054 float radiusSupportFactor 3.0 The multipler of the particle radius 1055 required for support. Larger radii 1056 ensure smooth results at the cost of 1057 performance. In the Gaussian kernel, the 1058 the radius is one standard deviation 1059 (sigma), so a `radiusSupportFactor` of 1060 3 corresponds to 3*sigma. 1061 1062 float clampMaxCumulativeValue 0 The maximum cumulative value possible, 1063 set by user. All cumulative values will 1064 be clamped to this, and further 1065 traversal (RBF summation) of particle 1066 contributions will halt when this value 1067 is reached. A value of zero or less 1068 turns this off. 1069 1070 bool estimateValueRanges true Enable heuristic estimation of value 1071 ranges which are used in internal 1072 acceleration structures for interval and 1073 hit iterators, as well as for 1074 determining the volume's overall value 1075 range. When set to `false`, the user 1076 *must* specify 1077 `clampMaxCumulativeValue`, and all value 1078 ranges will be assumed [0, 1079 `clampMaxCumulativeValue`]. Disabling 1080 this may improve volume commit time, but 1081 will make interval and hit iteration 1082 less efficient. 1083 -------- -------------------------- -------- --------------------------------------- 1084 : Configuration parameters for particle (`"particle"`) volumes. 1085 10861. Knoll, A., Wald, I., Navratil, P., Bowen, A., Reda, K., Papka, M.E. and 1087 Gaither, K. (2014), RBF Volume Ray Casting on Multicore and Manycore CPUs. 1088 Computer Graphics Forum, 33: 71-80. doi:10.1111/cgf.12363 1089 10902. K. Reda, A. Knoll, K. Nomura, M. E. Papka, A. E. Johnson and J. Leigh, 1091 "Visualizing large-scale atomistic simulations in ultra-resolution immersive 1092 environments," 2013 IEEE Symposium on Large-Scale Data Analysis and 1093 Visualization (LDAV), Atlanta, GA, 2013, pp. 59-65. 1094 1095Temporal Variation 1096------------------ 1097 1098Open VKL supports two types of temporal variation: temporally 1099structured and temporally unstructured. When one of these modes is enabled, the 1100volume can be sampled at different times. In both modes, time is assumed to 1101vary between zero and one. This can be useful for implementing renderers with 1102motion blur, for example. 1103 1104Temporal variation is generally configured through a parameter `temporalFormat`, 1105which accepts constants from the `VKLTemporalFormat` enum, though not all 1106modes may be supported by all volumes. On volumes that expect multiple input 1107nodes, the parameter is an array `node.temporalFormat`, and must provide one 1108value per node. 1109Multiple attributes in a voxel share the same temporal configuration. 1110Please refer to the individual volume sections above to find out supported 1111for each volume type. 1112 1113`temporalFormat` defaults to `VKL_TEMPORAL_FORMAT_CONSTANT` for all volume 1114types. This means that no temporal variation is present in the data. 1115 1116Temporally structured variation is configured by setting `temporalFormat` 1117to `VKL_TEMPORAL_FORMAT_STRUCTURED`. In this mode, the volume expects an 1118additional parameter `[node.]temporallyStructuredNumTimesteps`, which 1119specifies how many time steps are provided for all voxels, and must be at 1120least 2. A volume, or node, with $N$ voxels expects 1121$N * temporallyStructuredNumTimesteps$ values for each attribute. 1122The values are assumed evenly spaced over times $[0, 1]$: 1123$\{0, 1/(N-1), ..., 1\}$ 1124 1125Temporally unstructured variation supports differing time step counts and 1126sample times per voxel. 1127For $N$ input voxels, `temporallyUnstructuredIndices` is an array of $N+1$ 1128indices. Voxel $i$ has 1129$N_i = [temporallyUnstructuredIndices[i+1]-temporallyUnstructuredIndices[i])$ 1130temporal samples starting at index $temporallyUnstructuredIndices[i]$. 1131`temporallyUnstructuredTimes` specifies the times corresponding to the sample 1132values; the time values for each voxel must be 1133between zero and one and strictly increasing: $t0 < t1 < ... < tN$. To return 1134a value at sample time t, $t0 <= t <= tN$, Open VKL will interpolate linearly 1135from the two nearest time steps. Time values outside this range are clamped to 1136$[t0, tN]$. 1137 1138Sampler Objects 1139--------------- 1140 1141Computing the value of a volume at an object space coordinate is done using the 1142sampling API, and sampler objects. Sampler objects can be created using 1143 1144 VKLSampler vklNewSampler(VKLVolume volume); 1145 1146Sampler objects may then be parametrized with traversal parameters. Available 1147parameters are defined by volumes, and are a subset of the volume parameters. 1148As an example, `filter` can be set on both `vdb` volumes and their sampler objects. 1149The volume parameter is used as the default for sampler objects. The 1150sampler object parameter provides an override per ray. 1151More detail on parameters can be found in the sections on volumes. 1152Use `vklCommit()` to commit parameters to the sampler object. 1153 1154Sampling 1155-------- 1156 1157The scalar API takes a volume and coordinate, and returns a float value. The 1158volume's background value (by default `VKL_BACKGROUND_UNDEFINED`) is returned 1159for probe points outside the volume. The attribute index selects the scalar 1160attribute of interest; not all volumes support multiple attributes. The time 1161value, which must be between 0 and 1, specifies the sampling time. For 1162temporally constant volumes, this value has no effect. 1163 1164 float vklComputeSample(VKLSampler sampler, 1165 const vkl_vec3f *objectCoordinates, 1166 unsigned int attributeIndex, 1167 float time); 1168 1169Vector versions allow sampling at 4, 8, or 16 positions at once. Depending on 1170the machine type and Open VKL device implementation, these can give greater 1171performance. An active lane mask `valid` is passed in as an array of integers; 1172set 0 for lanes to be ignored, -1 for active lanes. An array of time values 1173corresponding to each object coordinate may be provided; a `NULL` value 1174indicates all times are zero. 1175 1176 void vklComputeSample4(const int *valid, 1177 VKLSampler sampler, 1178 const vkl_vvec3f4 *objectCoordinates, 1179 float *samples, 1180 unsigned int attributeIndex, 1181 const float *times); 1182 1183 void vklComputeSample8(const int *valid, 1184 VKLSampler sampler, 1185 const vkl_vvec3f8 *objectCoordinates, 1186 float *samples, 1187 unsigned int attributeIndex, 1188 const float *times); 1189 1190 void vklComputeSample16(const int *valid, 1191 VKLSampler sampler, 1192 const vkl_vvec3f16 *objectCoordinates, 1193 float *samples, 1194 unsigned int attributeIndex, 1195 const float *times); 1196 1197A stream version allows sampling an arbitrary number of positions at once. While 1198the vector version requires coordinates to be provided in a structure-of-arrays 1199layout, the stream version allows coordinates to be provided in an 1200array-of-structures layout. Thus, the stream API can be used to avoid 1201reformatting of data by the application. As with the vector versions, the stream 1202API can give greater performance than the scalar API. 1203 1204 void vklComputeSampleN(VKLSampler sampler, 1205 unsigned int N, 1206 const vkl_vec3f *objectCoordinates, 1207 float *samples, 1208 unsigned int attributeIndex, 1209 const float *times); 1210 1211All of the above sampling APIs can be used, regardless of the device's native 1212SIMD width. 1213 1214### Sampling Multiple Attributes 1215 1216Open VKL provides additional APIs for sampling multiple scalar attributes in a 1217single call through the `vklComputeSampleM*()` interfaces. Beyond convenience, 1218these can give improved performance relative to the single attribute sampling 1219APIs. As with the single attribute APIs, sampling time values may be specified; 1220note that these are provided per object coordinate only (rather than separately 1221per attribute). 1222 1223A scalar API supports sampling `M` attributes specified by `attributeIndices` on 1224a single object space coordinate: 1225 1226 void vklComputeSampleM(VKLSampler sampler, 1227 const vkl_vec3f *objectCoordinates, 1228 float *samples, 1229 unsigned int M, 1230 const unsigned int *attributeIndices, 1231 float time); 1232 1233Vector versions allow sampling at 4, 8, or 16 positions at once across the `M` 1234attributes: 1235 1236 void vklComputeSampleM4(const int *valid, 1237 VKLSampler sampler, 1238 const vkl_vvec3f4 *objectCoordinates, 1239 float *samples, 1240 unsigned int M, 1241 const unsigned int *attributeIndices, 1242 const float *times); 1243 1244 void vklComputeSampleM8(const int *valid, 1245 VKLSampler sampler, 1246 const vkl_vvec3f8 *objectCoordinates, 1247 float *samples, 1248 unsigned int M, 1249 const unsigned int *attributeIndices, 1250 const float *times); 1251 1252 void vklComputeSampleM16(const int *valid, 1253 VKLSampler sampler, 1254 const vkl_vvec3f16 *objectCoordinates, 1255 float *samples, 1256 unsigned int M, 1257 const unsigned int *attributeIndices, 1258 const float *times); 1259 1260The `[4, 8, 16] * M` sampled values are populated in the `samples` array in a 1261structure-of-arrays layout, with all values for each attribute provided in 1262sequence. That is, sample values `s_m,n` for the `m`th attribute and `n`th 1263object coordinate will be populated as 1264 1265 samples = [s_0,0, s_0,1, ..., s_0,N-1, 1266 s_1,0, s_1,1, ..., s_1,N-1, 1267 ..., 1268 s_M-1,0, s_M-1,1, ..., s_M-1,N-1] 1269 1270A stream version allows sampling an arbitrary number of positions at once across 1271the `M` attributes. As with single attribute stream sampling, the `N` 1272coordinates are provided in an array-of-structures layout. 1273 1274 void vklComputeSampleMN(VKLSampler sampler, 1275 unsigned int N, 1276 const vkl_vec3f *objectCoordinates, 1277 float *samples, 1278 unsigned int M, 1279 const unsigned int *attributeIndices, 1280 const float *times); 1281 1282The `M * N` sampled values are populated in the `samples` array in an 1283array-of-structures layout, with all attribute values for each coordinate 1284provided in sequence as 1285 1286 samples = [s_0,0, s_1,0, ..., s_M-1,0, 1287 s_0,1, s_1,1, ..., s_M-1,1, 1288 ..., 1289 s_0,N-1, s_1,N-1, ..., s_M-1,N-1] 1290 1291All of the above sampling APIs can be used, regardless of the device's native 1292SIMD width. 1293 1294Gradients 1295--------- 1296 1297In a very similar API to `vklComputeSample`, `vklComputeGradient` queries the 1298value gradient at an object space coordinate. Again, a scalar API, now 1299returning a vec3f instead of a float. NaN values are returned for points outside 1300the volume. The time value, which must be between 0 and 1, specifies the sampling 1301time. For temporally constant volumes, this value has no effect. 1302 1303 vkl_vec3f vklComputeGradient(VKLSampler sampler, 1304 const vkl_vec3f *objectCoordinates, 1305 unsigned int attributeIndex, 1306 float time); 1307 1308Vector versions are also provided: 1309 1310 void vklComputeGradient4(const int *valid, 1311 VKLSampler sampler, 1312 const vkl_vvec3f4 *objectCoordinates, 1313 vkl_vvec3f4 *gradients, 1314 unsigned int attributeIndex, 1315 const float *times); 1316 1317 void vklComputeGradient8(const int *valid, 1318 VKLSampler sampler, 1319 const vkl_vvec3f8 *objectCoordinates, 1320 vkl_vvec3f8 *gradients, 1321 unsigned int attributeIndex, 1322 const float *times); 1323 1324 void vklComputeGradient16(const int *valid, 1325 VKLSampler sampler, 1326 const vkl_vvec3f16 *objectCoordinates, 1327 vkl_vvec3f16 *gradients, 1328 unsigned int attributeIndex, 1329 const float *times); 1330 1331Finally, a stream version is provided: 1332 1333 void vklComputeGradientN(VKLSampler sampler, 1334 unsigned int N, 1335 const vkl_vec3f *objectCoordinates, 1336 vkl_vec3f *gradients, 1337 unsigned int attributeIndex, 1338 const float *times); 1339 1340All of the above gradient APIs can be used, regardless of the device's native 1341SIMD width. 1342 1343Iterators 1344--------- 1345 1346Open VKL has APIs to search for particular volume values along a ray. Queries 1347can be for ranges of volume values (`vklIterateInterval`) or for particular 1348values (`vklIterateHit`). 1349 1350Interval iterators require a context object to define the sampler and parameters 1351related to iteration behavior. An interval iterator context is created via 1352 1353 VKLIntervalIteratorContext vklNewIntervalIteratorContext(VKLSampler sampler); 1354 1355The parameters understood by interval iterator contexts are defined in the table 1356below. 1357 1358 -------------- ------------------------- ------------ ------------------------------------- 1359 Type Name Default Description 1360 -------------- ------------------------- ------------ ------------------------------------- 1361 int attributeIndex 0 Defines the volume attribute of 1362 interest. 1363 1364 vkl_range1f[] valueRanges [-inf, inf] Defines the value ranges of interest. 1365 Intervals not containing any of these 1366 values ranges may be skipped during 1367 iteration. 1368 1369 float intervalResolutionHint 0.5 A value in the range [0, 1] affecting 1370 the resolution (size) of returned 1371 intervals. A value of 0 yields the 1372 lowest resolution (largest) intervals 1373 while 1 gives the highest resolution 1374 (smallest) intervals. This value is 1375 only a hint; it may not impact 1376 behavior for all volume types. 1377 -------------- ------------------------- ------------ ------------------------------------- 1378 : Configuration parameters for interval iterator contexts. 1379 1380 1381Most volume types support the `intervalResolutionHint` parameter that can impact 1382the size of intervals returned duration iteration. These include `amr`, 1383`particle`, `structuredRegular`, `unstructured`, and `vdb` volumes. In all cases 1384a value of 1.0 yields the highest resolution (smallest) intervals possible, 1385while a value of 0.0 gives the lowest resolution (largest) intervals. In 1386general, smaller intervals will have tighter bounds on value ranges, and more 1387efficient space skipping behavior than larger intervals, which can be beneficial 1388for some rendering methods. 1389 1390For `structuredRegular`, `unstructured`, and `vdb` volumes, a value of 1.0 will 1391enable elementary cell iteration, such that each interval spans an individual 1392voxel / cell intersection. Note that interval iteration can be significantly 1393slower in this case. 1394 1395As with other objects, the interval iterator context must be committed before 1396being used. 1397 1398To query an interval, a `VKLIntervalIterator` of scalar or vector width must be 1399initialized with `vklInitIntervalIterator`. Time value(s) may be provided to 1400specify the sampling time. These values must be between 0 and 1; for the vector 1401versions, a `NULL` value indicates all times are zero. For temporally constant 1402volumes, the time values have no effect. 1403 1404 VKLIntervalIterator vklInitIntervalIterator(VKLIntervalIteratorContext context, 1405 const vkl_vec3f *origin, 1406 const vkl_vec3f *direction, 1407 const vkl_range1f *tRange, 1408 float time, 1409 void *buffer); 1410 1411 VKLIntervalIterator4 vklInitIntervalIterator4(const int *valid, 1412 VKLIntervalIteratorContext context, 1413 const vkl_vvec3f4 *origin, 1414 const vkl_vvec3f4 *direction, 1415 const vkl_vrange1f4 *tRange, 1416 const float *times, 1417 void *buffer); 1418 1419 VKLIntervalIterator8 vklInitIntervalIterator8(const int *valid, 1420 VKLIntervalIteratorContext context, 1421 const vkl_vvec3f8 *origin, 1422 const vkl_vvec3f8 *direction, 1423 const vkl_vrange1f8 *tRange, 1424 const float *times, 1425 void *buffer); 1426 1427 VKLIntervalIterator16 vklInitIntervalIterator16(const int *valid, 1428 VKLIntervalIteratorContext context, 1429 const vkl_vvec3f16 *origin, 1430 const vkl_vvec3f16 *direction, 1431 const vkl_vrange1f16 *tRange, 1432 const float *times, 1433 void *buffer); 1434 1435Open VKL places the iterator struct into a user-provided buffer, and the 1436returned handle is essentially a pointer into this buffer. This means that 1437the iterator handle must not be used after the buffer ceases to exist. 1438Copying iterator buffers is currently not supported. 1439 1440The required size, in bytes, of the buffer can be queried with 1441 1442 size_t vklGetIntervalIteratorSize(VKLIntervalIteratorContext context); 1443 1444 size_t vklGetIntervalIteratorSize4(VKLIntervalIteratorContext context); 1445 1446 size_t vklGetIntervalIteratorSize8(VKLIntervalIteratorContext context); 1447 1448 size_t vklGetIntervalIteratorSize16(VKLIntervalIteratorContext context); 1449 1450The values these functions return may change depending on the parameters set 1451on `sampler`. 1452 1453Open VKL also provides a conservative maximum size over all volume types as a 1454preprocessor definition (`VKL_MAX_INTERVAL_ITERATOR_SIZE`). For ISPC use cases, 1455Open VKL will attempt to detect the native vector width using `TARGET_WIDTH`, 1456which is defined in recent versions of ISPC, to provide a less conservative 1457size. 1458 1459Intervals can then be processed by calling `vklIterateInterval` as long as the 1460returned lane masks indicates that the iterator is still within the volume: 1461 1462 int vklIterateInterval(VKLIntervalIterator iterator, 1463 VKLInterval *interval); 1464 1465 void vklIterateInterval4(const int *valid, 1466 VKLIntervalIterator4 iterator, 1467 VKLInterval4 *interval, 1468 int *result); 1469 1470 void vklIterateInterval8(const int *valid, 1471 VKLIntervalIterator8 iterator, 1472 VKLInterval8 *interval, 1473 int *result); 1474 1475 void vklIterateInterval16(const int *valid, 1476 VKLIntervalIterator16 iterator, 1477 VKLInterval16 *interval, 1478 int *result); 1479 1480The intervals returned have a t-value range, a value range, and a 1481`nominalDeltaT` which is approximately the step size (in units of ray direction) 1482that should be used to walk through the interval, if desired. The number and 1483length of intervals returned is volume type implementation dependent. There is 1484currently no way of requesting a particular splitting. 1485 1486 typedef struct 1487 { 1488 vkl_range1f tRange; 1489 vkl_range1f valueRange; 1490 float nominalDeltaT; 1491 } VKLInterval; 1492 1493 typedef struct 1494 { 1495 vkl_vrange1f4 tRange; 1496 vkl_vrange1f4 valueRange; 1497 float nominalDeltaT[4]; 1498 } VKLInterval4; 1499 1500 typedef struct 1501 { 1502 vkl_vrange1f8 tRange; 1503 vkl_vrange1f8 valueRange; 1504 float nominalDeltaT[8]; 1505 } VKLInterval8; 1506 1507 typedef struct 1508 { 1509 vkl_vrange1f16 tRange; 1510 vkl_vrange1f16 valueRange; 1511 float nominalDeltaT[16]; 1512 } VKLInterval16; 1513 1514Querying for particular values is done using a `VKLHitIterator` in much the same 1515fashion. This API could be used, for example, to find isosurfaces. As with 1516interval iterators, time value(s) may be provided to specify the sampling time. 1517These values must be between 0 and 1; for the vector versions, a `NULL` value 1518indicates all times are zero. For temporally constant volumes, the time values 1519have no effect. 1520 1521Hit iterators similarly require a context object to define the sampler and other 1522iteration parameters. A hit iterator context is created via 1523 1524 VKLHitIteratorContext vklNewHitIteratorContext(VKLSampler sampler); 1525 1526The parameters understood by hit iterator contexts are defined in the table 1527below. 1528 1529 -------------- ---------------- ------------ ------------------------------------- 1530 Type Name Default Description 1531 -------------- ---------------- ------------ ------------------------------------- 1532 int attributeIndex 0 Defines the volume attribute of 1533 interest. 1534 1535 float[] values Defines the value(s) of interest. 1536 -------------- ---------------- ------------ ------------------------------------- 1537 : Configuration parameters for hit iterator contexts. 1538 1539The hit iterator context must be committed before being used. 1540 1541Again, a user allocated buffer must be provided, and a `VKLHitIterator` of the 1542desired width must be initialized: 1543 1544 VKLHitIterator vklInitHitIterator(VKLHitIteratorContext context, 1545 const vkl_vec3f *origin, 1546 const vkl_vec3f *direction, 1547 const vkl_range1f *tRange, 1548 float time, 1549 void *buffer); 1550 1551 VKLHitIterator4 vklInitHitIterator4(const int *valid, 1552 VKLHitIteratorContext context, 1553 const vkl_vvec3f4 *origin, 1554 const vkl_vvec3f4 *direction, 1555 const vkl_vrange1f4 *tRange, 1556 const float *times, 1557 void *buffer); 1558 1559 VKLHitIterator8 vklInitHitIterator8(const int *valid, 1560 VKLHitIteratorContext context, 1561 const vkl_vvec3f8 *origin, 1562 const vkl_vvec3f8 *direction, 1563 const vkl_vrange1f8 *tRange, 1564 const float *times, 1565 void *buffer); 1566 1567 VKLHitIterator16 vklInitHitIterator16(const int *valid, 1568 VKLHitIteratorContext context, 1569 const vkl_vvec3f16 *origin, 1570 const vkl_vvec3f16 *direction, 1571 const vkl_vrange1f16 *tRange, 1572 const float *times, 1573 void *buffer); 1574 1575Buffer size can be queried with 1576 1577 size_t vklGetHitIteratorSize(VKLHitIteratorContext context); 1578 1579 size_t vklGetHitIteratorSize4(VKLHitIteratorContext context); 1580 1581 size_t vklGetHitIteratorSize8(VKLHitIteratorContext context); 1582 1583 size_t vklGetHitIteratorSize16(VKLHitIteratorContext context); 1584 1585Open VKL also provides the macro `VKL_MAX_HIT_ITERATOR_SIZE` as a conservative 1586estimate. 1587 1588Hits are then queried by looping a call to `vklIterateHit` as long as the 1589returned lane mask indicates that the iterator is still within the volume. 1590 1591 int vklIterateHit(VKLHitIterator iterator, VKLHit *hit); 1592 1593 void vklIterateHit4(const int *valid, 1594 VKLHitIterator4 iterator, 1595 VKLHit4 *hit, 1596 int *result); 1597 1598 void vklIterateHit8(const int *valid, 1599 VKLHitIterator8 iterator, 1600 VKLHit8 *hit, 1601 int *result); 1602 1603 void vklIterateHit16(const int *valid, 1604 VKLHitIterator16 iterator, 1605 VKLHit16 *hit, 1606 int *result); 1607 1608Returned hits consist of a t-value, a volume value (equal to one of the 1609requested values specified in the context), and an (object space) epsilon value 1610estimating the error of the intersection: 1611 1612 typedef struct 1613 { 1614 float t; 1615 float sample; 1616 float epsilon; 1617 } VKLHit; 1618 1619 typedef struct 1620 { 1621 float t[4]; 1622 float sample[4]; 1623 float epsilon[4]; 1624 } VKLHit4; 1625 1626 typedef struct 1627 { 1628 float t[8]; 1629 float sample[8]; 1630 float epsilon[8]; 1631 } VKLHit8; 1632 1633 typedef struct 1634 { 1635 float t[16]; 1636 float sample[16]; 1637 float epsilon[16]; 1638 } VKLHit16; 1639 1640For both interval and hit iterators, only the vector-wide API for the native 1641SIMD width (determined via `vklGetNativeSIMDWidth` can be called. The scalar 1642versions are always valid. This restriction will likely be lifted in the future. 1643 1644Performance Recommendations 1645=========================== 1646 1647MXCSR control and status register 1648--------------------------------- 1649 1650It is strongly recommended to have the `Flush to Zero` and `Denormals are Zero` 1651mode of the MXCSR control and status register enabled for each thread before 1652calling the sampling, gradient, or interval API functions. Otherwise, under some 1653circumstances special handling of denormalized floating point numbers can 1654significantly reduce application and Open VKL performance. The device parameter 1655`flushDenormals` or environment variable `OPENVKL_FLUSH_DENORMALS` can be used 1656to toggle this mode; by default it is enabled. Alternatively, when using Open 1657VKL together with the Intel® Threading Building Blocks, it is sufficient to 1658execute the following code at the beginning of the application main thread 1659(before the creation of the `tbb::task_scheduler_init` object): 1660 1661 #include <xmmintrin.h> 1662 #include <pmmintrin.h> 1663 ... 1664 _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); 1665 _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON); 1666 1667If using a different tasking system, make sure each thread calling into 1668Open VKL has the proper mode set. 1669 1670Iterator Allocation 1671------------------- 1672 1673`vklInitIntervalIterator` and `vklInitHitIterator` expect a user allocated 1674buffer. While this buffer can be allocated by any means, we expect iterators 1675to be used in inner loops and advise against heap allocation in that case. 1676Applications may provide high performance memory pools, but as a preferred 1677alternative we recommend stack allocated buffers. 1678 1679In C99, variable length arrays provide an easy way to achieve this: 1680 1681 const size_t bufferSize = vklGetIntervalIteratorSize(sampler); 1682 char buffer[bufferSize]; 1683 1684Note that the call to `vklGetIntervalIteratorSize` or `vklGetHitIteratorSize` 1685should not appear in an inner loop as it is relatively costly. The return value 1686depends on the volume type, target architecture, and parameters to `sampler`. 1687 1688In C++, variable length arrays are not part of the standard. Here, users may 1689rely on `alloca` and similar functions: 1690 1691 #include <alloca.h> 1692 const size_t bufferSize = vklGetIntervalIteratorSize(sampler); 1693 void *buffer = alloca(bufferSize); 1694 1695Similarly for ISPC, variable length arrays are not supported, but `alloca` may 1696be used: 1697 1698 const uniform size_t bufferSize = vklGetIntervalIteratorSizeV(sampler); 1699 void *uniform buffer = alloca(bufferSize); 1700 1701Users should understand the implications of `alloca`. In particular, 1702`alloca` does check available stack space and may result in stack overflow. 1703`buffer` also becomes invalid at the end of the scope. As one consequence, it 1704cannot be returned from a function. 1705On Windows, `_malloca` is a safer option that performs additional error 1706checking, but requires the use of `_freea`. 1707 1708Applications may instead rely on the `VKL_MAX_INTERVAL_ITERATOR_SIZE` and 1709`VKL_MAX_HIT_ITERATOR_SIZE` macros. For example, in ISPC: 1710 1711 uniform unsigned int8 buffer[VKL_MAX_INTERVAL_ITERATOR_SIZE]; 1712 1713These values are majorants over all devices and volume types. Note that Open VKL 1714attempts to detect the target SIMD width using `TARGET_WIDTH`, returning smaller 1715buffer sizes for narrow architectures. However, Open VKL may fall back to the 1716largest buffer size over all targets. 1717 1718Multi-attribute Volume Data Layout 1719---------------------------------- 1720 1721Open VKL provides flexible managed data APIs that allow applications to specify 1722input data in various formats and layouts. When shared buffers are used 1723(`dataCreationFlags = VKL_DATA_SHARED_BUFFER`), Open VKL will use the 1724application-owned memory directly, respecting the input data layout. Shared 1725buffers therefore allow applications to strategically select the best layout for 1726multi-attribute volume data and expected sampling behavior. 1727 1728For volume attributes that are sampled individually (e.g. using 1729`vklComputeSample[4,8,16,N]()`), it is recommended to use a structure-of-arrays 1730layout. That is, each attribute's data should be compact in contiguous memory. 1731This can be accomplished by simply using Open VKL owned data objects 1732(`dataCreationFlags = VKL_DATA_DEFAULT`), or by using a natural `byteStride` for 1733shared buffers. 1734 1735For volume attributes that are sampled simultaneously (e.g. using 1736`vklComputeSampleM[4,8,16,N]()`), it is recommended to use an 1737array-of-structures layout. That is, data for these attributes should be 1738provided per voxel in a contiguous layout. This is accomplished using shared 1739buffers for each attribute with appropriate byte strides. For example, for a 1740three attribute structured volume representing a velocity field, the data can be 1741provided as: 1742 1743 // used in Open VKL shared buffers, so must not be freed by application 1744 std::vector<vkl_vec3f> velocities(numVoxels); 1745 1746 for (auto &v : velocities) { 1747 v.x = ...; 1748 v.y = ...; 1749 v.z = ...; 1750 } 1751 1752 std::vector<VKLData> attributes; 1753 1754 attributes.push_back(vklNewData(device, 1755 velocities.size(), 1756 VKL_FLOAT, 1757 &velocities[0].x, 1758 VKL_DATA_SHARED_BUFFER, 1759 sizeof(vkl_vec3f))); 1760 1761 attributes.push_back(vklNewData(device, 1762 velocities.size(), 1763 VKL_FLOAT, 1764 &velocities[0].y, 1765 VKL_DATA_SHARED_BUFFER, 1766 sizeof(vkl_vec3f))); 1767 1768 attributes.push_back(vklNewData(device, 1769 velocities.size(), 1770 VKL_FLOAT, 1771 &velocities[0].z, 1772 VKL_DATA_SHARED_BUFFER, 1773 sizeof(vkl_vec3f))); 1774 1775 VKLData attributesData = 1776 vklNewData(device, attributes.size(), VKL_DATA, attributes.data()); 1777 1778 for (auto &attribute : attributes) 1779 vklRelease(attribute); 1780 1781 VKLVolume volume = vklNewVolume(device, "structuredRegular"); 1782 1783 vklSetData(volume, "data", attributesData); 1784 vklRelease(attributesData); 1785 1786 // set other volume parameters... 1787 1788 vklCommit(volume); 1789 1790These are general recommendations for common scenarios; it is still recommended 1791to evaluate performance of different volume data layouts for your application's 1792particular use case. 1793