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