In this chapter we'll talk about possible optimizations when integrating AppSDK for different languages.
Language agnostic
Material previews
If you implement a material preview pane in your application, you will need to start multiple renders of the same simple scene with different materials applied. You may notice that renderer initialization isn't particularly fast due to internal reasons, which will slow the load time of the pane. The workaround is to create an Interactive renderer instead of production, set keepInteractiveRunning=true
and just apply the different materials to the same base object loaded from a preset scene. This should be used only in a for-loop. When the list of materials to be previewed is exhausted the renderer should be stopped to release its resources.
Commit group of changes
In Interactive mode, use the autoCommit=false
option of the renderer in addition to explicitly calling renderer.commit()
to allow for a group of changes to be applied together. This will decrease the total time needed to apply the changes.
GUI message processing
As the GUI message processing is enabled by default, disabling it during internal waits could improve the overall responsiveness of the integration software. The interface is called setGUIMessageProcessing(bool enable)
.
Memory and performance optimizations
- A performance optimization could come frоm transferring efficiently large data (vertices, map_channels, raw bitmap buffers) to V-Ray memory.
- Aside from external files, large data is contained mostly in typed lists such as VectorList and IntList (which can also be nested inside general heterogenic lists as is the case with map_channels).
Plugin properties (parameters) are always set and read by value in all supported programming languages.
List plugin parameter optimization tips
In all supported programming languages AppSDK uses "smart" plugin parameter value setters that can guess and efficiently convert data in many cases:
- If e.g. the definition type of a plugin property being set is VectorList and you try to assign an array or a list of 3*N floats or doubles to it, the data will be automatically reinterpreted or converted to a VectorList of N elements often more efficiently than doing it in your code. This can be useful in cases where you receive the data in a format different from the expected one.
- So code like this will work (Python/js-like syntax is used in the examples below but the technique would work in all supported languages):
mesh.vertices = [1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9] # values are automatically converted
ormesh.vertices = FloatList(1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9) # value is directly reinterpreted
mesh.vertices
value is nowVectorList(Vector(1.1, 2.2, 3.3), Vector(4.4, 5.5, 6.6), Vector(7.7, 8.8, 9.9))
- Note that e.g. std::vector<double> in C++ and List<double> or double[] in C# will be automatically converted to FloatList, VectorList or ColorList efficiently depending on the property definition type.
But to use it you'd have to call the generalPlugin.setValue(propertyName, value)
function because the property setters in C++ and properties in C# are strongly typed and wouldn't accept data of any other type.
Node.js specific
Plugin properties (parameters) are always set and read by value, not by reference!
Sync/async
- Many methods in the Node.js binding offer both synchronous and asynchronous versions. When you don't have to wait for the whole process to complete, you can use the asynchronous version.
- In addition to the standard synchronous VRayRenderer object construction, the asynchronous API
vray.createVRayRenderer(callback, options)
is also available. - Similarly,
vray.
createVRayServer
(callback, options)
could be used to construct a VRayServer object asynchronously. - The following VRayRenderer methods have sync/async versions:
start, export, pickPlugin, pickPlugins, load, append, loadAsText, appendAsText, loadFromBuffer, appendFromBuffer, loadFiltered, appendFiltered, loadAsTextFiltered, appendAsTextFiltered, appendFromBufferFiltered, saveIrradianceMapFile, saveLightCacheFile, savePhotonMapFile, saveCausticsFile, addHosts, removeHosts, resetHosts, getAllHosts, getActiveHosts, getInactiveHosts
- The following VRayServer methods have sync/async versions:
addHosts, removeHosts, resetHosts, getAllHosts, getActiveHosts, getInactiveHosts
- The following VRayImage methods have sync/async versions:
createFromBuffer,
save, compress, getDownscaled, getResized, getDownscaledCropped, getResizedCropped, getFitIn, getFitOut, getCutIn, load, loadSize
- The following VFB methods have sync/async versions:
saveImage, loadImage
Typed Lists
- General lists in Node.js use JavaScript
Array
/[]
. Typed lists are based on JavaScript typed arrays. FloatList
,VectorList
andColorList
are very thin wrappers aroundFloat32Array
and can be freely used as such (and Float32Array can be used instead in place of them in most cases).- Similarly
IntList
is a thin wrapper aroundInt32Array
. - When you read plugin properties, you always get a copy of the underlying list data.
- However, when you set plugin properties, you may opt to set the Javascript typed array data directly into V-Ray memory. The behavior is controlled per VRayRenderer instance by its
VRayRenderer.maxTypedListsCopyByteSize
property. If a typed list data size in bytes is above the threshold specified bymaxTypedListsCopyByteSize
, the data contained in the Javascript typed array will be set directly to V-Ray memory. The default value ofmaxTypedListsCopyByteSize
is -1 which means data will always be copied. You can changed it to 0 or to a small value (e.g. 200) to reduce memory consumption and make the code faster. - V-Ray typed lists in Node.js once created, they cannot be resized (because typed arrays in JavaScript cannot be resized).
If needed, you may freely "convert" among all the different types of typed lists, typed arrays and node buffers by accessing their underlying ArrayBuffer
binary data storage and passing it to the new type constructor:
var a = FloatList(0.1, 0.2, 0.3, 0.4, 0.5, 0.6); // let's have a float list var b = Buffer.from(a.buffer); // now the buffer 'b' shares its bytes with 'a' var c = new VectorList(b.buffer); // now the VectorList shares its data with buffer 'b' and the list 'a' // 'c' is VectorList(Vector(0.1, 0.2, 0.3), Vector(0.4, 0.5, 0.6))
Since v6.20
Two new typed-list types were added in version 6.20:
PluginList
is a resizable plugin container that inherits from JavaScriptArray
but can store (only) plugin objects efficiently. Now functions like VRayRenderer.classes.Node.getInstances() returnPluginList
instead of js array which allows hundreds of thousand instances to be returned without much memory overhead. In most cases the already written user code will continue to work without noticing the underlying change. Also, user code can constructPluginList
s explicitly and pass them to plugin property setters where lists of plugins are expected (but this is not mandatory).TransformList
is a non-resizable array-likeTransform
objects container, somewhat similar toVectorList
,ColorList
, etc. but instead of being a thin wrapper aroundFloat32Array
, it contains its data in aFloatList
/Float32Array
internally. So when you access e.g. the elementtransList[index]
you'll get or set aTransform
object, not just a number as is the case withVectorList
orColorList
.
You can access the underlyingFloatList
data directly using theTransformList.floatList
property.
The functionGeomUtils.readScatterData()
returns aTransformList
as part of the returned data.
Also, user code can constructTransformList
s explicitly (e.g. by wrapping existingFloatList
/Float32Array
instances) and pass them to plugin property setters where lists of transforms are expected.
C++ specific
Typed Lists
- The typed lists in C++ are based on
std::vector
, e.g.IntList
isstd::vector<int>
,VectorList
isstd::vector<Vector>
, etc. The general listValueList
is also based on std::vector and isstd::vector<Value>
- The
Value
type is a union of all types that can be inserted in a list and can contain typed lists orValueList
. - In C++ it is possible to set the data contained in long typed lists directly to V-Ray memory without copying leveraging the power of C++11 move semantics.
- All lists (based on
std::vector
), theValue
type and plugin property setters support both copy and move semantics. - For small data sizes (short lists up to several hundred elements) it is not beneficial to use move semantics as it has a small (a few bytes) dynamic memory allocation cost. But even if used, the data will be moved only above a certain threshold.
Plugin property setters selectively move or copy data depending on the type and size of the lists:
VectorList vectors(500000); // A large list of vectors // Fill in the list... // This will set a copy of vectors inside V-Ray memory GeomStaticMesh.set_vertices(vectors); // But this will get the vertices data pointer and set it directly into V-Ray memory avoiding the expensive copy operation GeomStaticMesh.set_vertices(std::move(vectors));
Or in a more complex example:
VectorList vectors(500000); // A large list of vectors IntList ints(1500000); // A large list of ints // Fill in the lists... ValueList channel; channel.push_back(Value(1)); channel.push_back(Value(std::move(vectors))); // Move vectors data to Value and Value to ValueList channel.push_back(Value(std::move(ints))); // Move ints data to Value and Value to ValueLis ValueList mapChannels; mapChannels.push_back(Value(std::move(channel))); // Move channel data to Value and Value to mapChannels mesh.set_map_channels(std::move(mapChannels)); // Move large data (vectors and ints) to V-Ray memory and copy the rest
Ref lists
This is an advanced feature, enabled by the VRAY_SDK_INTEROPERABILITY
macro. Use these types for large lists of primitives. The available types are: IntRefList, FloatRefList, VectorRefList, ColorRefList, CharString, CharStringRefList, Value, ValueRefList. They are also suitable for migrating existing code written against the V-Ray SDK to the AppSDK.
C# specific
Plugin properties (parameters) are always set and read by value, not by reference!
Typed Lists and Arrays
- The data from/to .Net managed memory is always copied to/from V-Ray unmanaged memory. But we need to minimize additional unnecessary copies.
- Typed lists in .Net are presented as
IList<T>
and are mostly based onList<T>
. AndList<T>
(cast toIList<T>
) is what you'll get when you read a plugin property. - Plugin property setters also accept
IList<T>
. Although any type supporting the interface would work, the underlying code is highly optimized forList<T>
andT[]
. - It is very important to note that arrays in .Net support the
IList<T>
interface explicitly. So if your data is contained in aT[]
array, assign it directly to any property or pass it directly to any function that acceptsIList<T>
! It's counter-productive to convertT[]
toList<T>
in order to pass it asIList<T>
and it will make unnecessary memory allocations and copies. - When you know the length of the list beforehand, create it with the desired size. This will avoid unnecessary reallocations caused by extending the container. Or even better, just use plain
T[length]
array.
In summary
- Use
T[]
orList<T>
when you create lists for V-Ray. - Do NOT try to convert
T[]
toList<T>
, use it directly! (T[]
is convertible toIList<T>
). - Do NOT try to convert
List<T>
toT[]
, use it directly. - Pre-allocate the lists with the needed length (or just use arrays) where possible.
Python specific
Plugin properties (parameters) are always set and read by value, not by reference!
Typed Lists
- The General lists in Python use Python
lists
/[]
. - The V-Ray typed lists, however, use dedicated types -
IntList
,FloatList
,VectorList
,ColorList
and, since version 6.20, alsoTransformList
andPluginList
and they all support the sequence protocol. This means that in most cases they can be used as Python lists but they are much more efficient. - All standard list operations like
a.append(4)
andb+=a
work as well. - Typed lists also support the memory view buffer protocol.
To be able to be as efficient as possible and to cover all practical cases typed lists have some peculiarities. E.g. their positional and keyword arguments are entirely different:
a=IntList(1, 2, 3) # create an int list 'a' of 3 elements and a[0]=1, a[1]=2, a[2]=3 b=IntList(count=300) # create an int list 'b' with 300 elements initialized to 0. You can set their values later. s=[1, 2, 3, 4] # if you already have a different sequence and want to create a typed array from its elements a=IntList(source=s) # the 'source' keyword can be used.