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()