Cook Interface (C++) ==================== C++ Reference ------------- .. doxygengroup:: FnGeolibOp Geolib Op Private Data ---------------------- Private Data Overview ````````````````````` When creating child locations, developers of Ops can pass user-defined data to the created child locations in one of two ways: - **Using Op Args**: If you wish to pass data to child locations, the best way to do this is through their Op Args. The `Foundry::Katana::GeolibCookInterface::getOpArg() <#_CPPv2N7Foundry6Katana19GeolibCookInterface8getOpArgEN10FnPlatform10StringViewE>`_ function gives all arguments as a GroupAttribute. - **Using private data**: If you need to pass more complex data down to children that is not representable as attributes, then you can additionally use the private data pointer. For instance: - Associative maps - Modifiable data - Thread-safe data accessors - Performance-critical data - Other raw data Private data can be passed as a void pointer to the following functions: - ``Foundry::Katana::GeolibCookInterface::createChild()`` - ``Foundry::Katana::GeolibCookInterface::replaceChildTraversalOp()`` - ``Foundry::Katana::GeolibCookInterface::execOp()`` A utility class is defined; `Foundry::Katana::GeolibPrivateData <#_CPPv2N7Foundry6Katana17GeolibPrivateDataE>`_; that can be used as a base class for custom private data classes. For example, the class is used in the AlembicIn Op for its own private data type: .. code-block:: cpp class AlembicInPrivateData : public Foundry::Katana::GeolibPrivateData To retrieve the private data, the `Foundry::Katana::GeolibCookInterface::getPrivateData() <#_CPPv2N7Foundry6Katana19GeolibCookInterface14getPrivateDataEv>`_ function can be used. Example Op that uses private data ````````````````````````````````` The following example shows how private data can be used to pass complex information to the created child locations. The first time the Op runs at a specific location, a file reader is created in order to open a file. Because this operation is usually expensive, we want to reuse the file reader to retrieve the required information for all the locations. However, the file reader is not suitable for being stored in a FnAttribute, therefore, it will need to be wrapped into a private data container (derived from `Foundry::Katana::GeolibPrivateData <#_CPPv2N7Foundry6Katana17GeolibPrivateDataE>`_ in this case), then to be passed down to the created child locations. .. code-block:: cpp /// Example class that reads a metaphorical custom format file and converts its /// data into FnAttributes. class CustomFormatFileReader final { public: CustomFormatFileReader(const std::string& filepath) { ... } ~CustomFormatFileReader() { ... } std::vector getChildren(const std::string& parent) { ... } FnAttribute::GroupAttribute getAttrs(const std::string& location) { ... } private: ... }; /// Private data container to pass data to child locations. class PrivateData : public Foundry::Katana::GeolibPrivateData { public: PrivateData(const std::shared_ptr& fileReader) : m_fileReader(fileReader) {} std::shared_ptr m_fileReader; }; /// Example Op that reads a custom format file and creates the hierarchy in /// Katana along with all its attributes. class CustomFormatIn : public Foundry::Katana::GeolibOp { public: static void setup(Foundry::Katana::GeolibSetupInterface& interface) { interface.setThreading( Foundry::Katana::GeolibSetupInterface::ThreadModeConcurrent); } static void cook(Foundry::Katana::GeolibCookInterface& interface) { std::shared_ptr fileReader; if (interface.atRoot()) { // Open file only when at root. const FnAttribute::StringAttribute filepathAttr( interface.getOpArg("filepath")); const std::string filepath = filepathAttr.getValue("", false); fileReader = std::make_shared(filepath); } else { // Get previously opened file from private data. const PrivateData* const privateData = static_cast(interface.getPrivateData()); assert(privateData && "Private data not found where it should"); if (!privateData) return; fileReader = privateData->m_fileReader; } const std::string relativeOutputPath = interface.getRelativeOutputLocationPath(); // Set attributes for this relative location. const auto attrs = fileReader->getAttrs(relativeOutputPath); const int64_t attrCount = attrs.getNumberOfChildren(); for (int64_t i = 0; i < attrCount; ++i) { interface.setAttr(attrs.getChildName(i), attrs.getChildByIndex(i)); } // Get relative children from file. const auto childLocations = fileReader->getChildren(relativeOutputPath); // Create one child location per entry, with their respective // attributes. The file reader will be passed as private data. for (const auto& child : childLocations) { interface.createChild( child, "CustomFormatIn", FnAttribute::Attribute(), Foundry::Katana::GeolibCookInterface::ResetRootAuto, new PrivateData(fileReader), PrivateData::Delete); } } }; Private Data Life Cycle ``````````````````````` The private data passed to an Op is linked to the lifetime of the location data of the location that the Op was involved in creating. Location data is deleted either when the location is evicted, or when the location is re-cooked after having been invalidated. During a UI session, eviction only takes place when caches are flushed. Therefore, unless the location needs to be re-cooked, private data may live for long periods. During a render, unless explicit eviction is requested by the renderer (see the ``evict`` parameter present in several functions in :doc:`Scene Graph Iterators <../../Plugins/Utilities/SceneGraphIterator>`), Geolib will only evict location data that is not currently in the ancestral path for the location that it is currently cooking, with the exception of location data that was cooked as a result of a scattered query. .. figure:: BasicEvictionCase.png :alt: Basic Eviction Case Basic eviction case: After the marked location is cooked, locations that are not in the ancestral path will get their location data evicted, along with the associated Op's private data that was used to create the location. .. The figure above was generated with the following graph: graph basic_eviction_case { size="4,4" a -- b [color=gray70]; a -- c [color=gray70]; a -- d; b -- e [color=gray70]; b -- f [color=gray70]; d -- g; g -- h; g -- i [color=gray70]; h -- j [color=gray70]; h -- k [color=gray70]; i -- l [color=gray70]; k -- m [color=gray70]; k -- n [color=gray70]; a [label="", shape=circle]; b [label="", shape=circle, color=gray70]; c [label="", shape=circle, color=gray70]; d [label="", shape=circle]; e [label="", shape=circle, color=gray70]; f [label="", shape=circle, color=gray70]; g [label="", shape=circle]; h [label="", shape=doublecircle]; i [label="", shape=circle, color=gray70]; j [label="", shape=circle, color=gray70]; k [label="", shape=circle, color=gray70]; l [label="", shape=circle, color=gray70]; m [label="", shape=circle, color=gray70]; n [label="", shape=circle, color=gray70]; } When private data is used with ``Foundry::Katana::GeolibCookInterface::execOp()``, its lifetime is short. As soon as the function completes, the provided deleter function will be invoked and the private data will be deleted.