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-2024 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 = Token("inputs:value");
77 auto outToken = Token("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()