Tutorial 29 - Node with simple ABI computeVectorized#
This tutorial demonstrates how to compose nodes that implements a very simple computeVectorized function using directly ABIs. It shows how to access the data, using the different available methods.
OgnTutorialVectorizedABIPassThrough.cpp#
The cpp file contains the implementation of the node. It takes a floating point input and just copy it to its output, demonstrating how to handle a vectorized compute. It shows what would be the implementation for a regular compute function, and the different way it could implement a computeVectorized function. - method #1: by indexing attribute retrieval ABI function directly in a loop - method #2: by mutating the attribute data handle in a loop - method #3: by retrieving the raw data, and working directly with it
1// SPDX-FileCopyrightText: Copyright (c) 2023-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2// SPDX-License-Identifier: LicenseRef-NvidiaProprietary
3//
4// NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
5// property and proprietary rights in and to this material, related
6// documentation and any modifications thereto. Any use, reproduction,
7// disclosure or distribution of this material and related documentation
8// without an express license agreement from NVIDIA CORPORATION or
9// its affiliates is strictly prohibited.
10#include <OgnTutorialVectorizedABIPassthroughDatabase.h>
11
12// The method used by the computation to perform the passthrough
13// 0 = simple copy, no vectorization
14#define TUTO_ABI_PASSTHROUGH_METHOD_SIMPLE 0
15
16// 1 = vectorized copy by indexing the instance directly per attribute
17#define TUTO_ABI_PASSTHROUGH_METHOD_ATTR 1
18
19// 2 = vectorized copy by mutating attribute data handles
20#define TUTO_ABI_PASSTHROUGH_METHOD_MUTATE 2
21
22// 3 = vectorized copy using raw data: this is the most efficient method
23#define TUTO_ABI_PASSTHROUGH_METHOD_RAW 3
24
25// By default here, use the least popular method to make sure it remains tested
26#define TUTO_ABI_PASSTHROUGH_METHOD TUTO_ABI_PASSTHROUGH_METHOD_ATTR
27
28// This node perform a copy of its input to its output
29class OgnTutorialVectorizedABIPassthrough
30{
31public:
32#if TUTO_ABI_PASSTHROUGH_METHOD == TUTO_ABI_PASSTHROUGH_METHOD_SIMPLE
33 // begin-regular
34 static bool compute(GraphContextObj const& contextObj, NodeObj const& nodeObj)
35 {
36 NodeContextHandle nodeHandle = nodeObj.nodeContextHandle;
37
38 auto inputValueAttr = getAttributeR(contextObj, nodeHandle, Token("inputs:value"), kAccordingToContextIndex);
39 const float* inputValue = getDataR<float>(contextObj, inputValueAttr);
40
41 auto outputValueAttr = getAttributeW(contextObj, nodeHandle, Token("outputs:value"), kAccordingToContextIndex);
42 float* outputValue = getDataW<float>(contextObj, outputValueAttr);
43
44 if (inputValue && outputValue)
45 {
46 *outputValue = *inputValue;
47 return true;
48 }
49
50 return false;
51 }
52 // end-regular
53#elif TUTO_ABI_PASSTHROUGH_METHOD == TUTO_ABI_PASSTHROUGH_METHOD_ATTR
54 // begin-attr
55 static size_t computeVectorized(GraphContextObj const& contextObj, NodeObj const& nodeObj, size_t count)
56 {
57 GraphContextObj const* contexts = nullptr;
58 NodeObj const* nodes = nullptr;
59
60 // When using auto instancing, similar graphs can get merged together, and computed vectorized
61 // In such case, each instance represent a different node in a different graph
62 // Accessing the data either through the provided node, or through the actual auto-instance node would work
63 // properly But any other ABI call requiring the node would need to provide the proper node. While not necessary
64 // in this context, do the work of using the proper auto-instance node in order to demonstrate how to use it.
65 size_t handleCount = nodeObj.iNode->getAutoInstances(nodeObj, contexts, nodes);
66
67 auto nodeHandle = [&](InstanceIndex index) -> NodeContextHandle
68 { return nodes[handleCount == 1 ? 0 : index.index].nodeContextHandle; };
69
70 auto context = [&](InstanceIndex index) -> GraphContextObj
71 { return contexts[handleCount == 1 ? 0 : index.index]; };
72
73 size_t ret = 0;
74 const float* inputValue{ nullptr };
75 float* outputValue{ nullptr };
76 auto inToken = NameToken("inputs:value");
77 auto outToken = NameToken("outputs:value");
78
79 for (InstanceIndex idx{ 0 }; idx < InstanceIndex{ count }; ++idx)
80 {
81 auto inputValueAttr = getAttributeR(context(idx), nodeHandle(idx), inToken, idx);
82 inputValue = getDataR<float>(context(idx), inputValueAttr);
83
84 auto outputValueAttr = getAttributeW(context(idx), nodeHandle(idx), outToken, idx);
85 outputValue = getDataW<float>(context(idx), outputValueAttr);
86
87 if (inputValue && outputValue)
88 {
89 *outputValue = *inputValue;
90 ++ret;
91 }
92 }
93
94 return ret;
95 }
96 // end-attr
97#elif TUTO_ABI_PASSTHROUGH_METHOD == TUTO_ABI_PASSTHROUGH_METHOD_MUTATE
98 // begin-mutate
99 static size_t computeVectorized(GraphContextObj const& contextObj, NodeObj const& nodeObj, size_t count)
100 {
101 NodeContextHandle nodeHandle = nodeObj.nodeContextHandle;
102
103 size_t ret = 0;
104 const float* inputValue{ nullptr };
105 float* outputValue{ nullptr };
106 auto inputValueAttr = getAttributeR(contextObj, nodeHandle, Token("inputs:value"), kAccordingToContextIndex);
107 auto outputValueAttr = getAttributeW(contextObj, nodeHandle, Token("outputs:value"), kAccordingToContextIndex);
108
109 while (count--)
110 {
111 inputValue = getDataR<float>(contextObj, inputValueAttr);
112 outputValue = getDataW<float>(contextObj, outputValueAttr);
113
114 if (inputValue && outputValue)
115 {
116 *outputValue = *inputValue;
117 ++ret;
118 }
119 inputValueAttr = contextObj.iAttributeData->moveToAnotherInstanceR(contextObj, inputValueAttr, 1);
120 outputValueAttr = contextObj.iAttributeData->moveToAnotherInstanceW(contextObj, outputValueAttr, 1);
121 }
122
123 return ret;
124 }
125 // end-mutate
126#elif TUTO_ABI_PASSTHROUGH_METHOD == TUTO_ABI_PASSTHROUGH_METHOD_RAW
127 // begin-raw
128 static size_t computeVectorized(GraphContextObj const& contextObj, NodeObj const& nodeObj, size_t count)
129 {
130 NodeContextHandle nodeHandle = nodeObj.nodeContextHandle;
131
132 auto inputValueAttr = getAttributeR(contextObj, nodeHandle, Token("inputs:value"), kAccordingToContextIndex);
133 const float* inputValue = getDataR<float>(contextObj, inputValueAttr);
134
135 auto outputValueAttr = getAttributeW(contextObj, nodeHandle, Token("outputs:value"), kAccordingToContextIndex);
136 float* outputValue = getDataW<float>(contextObj, outputValueAttr);
137
138 if (inputValue && outputValue)
139 {
140 memcpy(outputValue, inputValue, count * sizeof(float));
141 return count;
142 }
143
144 return 0;
145 }
146 // end-raw
147#endif
148};
149
150REGISTER_OGN_NODE()