Using Standalone Notice

The USD Notice Framework provides a UnfNotice::StageNotice interface which can be used to create standalone notices related to the USD stage.

Standalone notices present the following features:

  1. They do not reference data from the stage

This rule ensures that notices are safe to use in asynchronous context. By contrast, Usd notices such as UsdNotice::ObjectsChanged and UsdNotice::LayerMutingChanged both reference data from the stage and thus are not safe to use when the stage is not longer reachable.

  1. They can include logic for consolidation with notice of the same type

This makes it possible to reduce the number of notices emitted during a transaction without loosing information.

Using notice transaction

A transaction is a time frame during which the emission of standalone notices needs to be withheld. The Broker is in charge of capturing these notices via a NoticeTransaction instance:

auto stage = PXR_NS::UsdStage::CreateInMemory();
auto broker = unf::Broker::Create(stage);

{
    unf::NoticeTransaction transaction(broker);

    // Emission of standalone notices is deferred until the end of the
    // transaction. Other notices are sent as normal.
}

A NoticeTransaction instance can also be constructed directly from the Usd stage, which encapsulates the creation of the broker if none have been previously created for this stage:

auto stage = PXR_NS::UsdStage::CreateInMemory();

{
    unf::NoticeTransaction transaction(stage);

    // ...
}

At the end of a transaction, all notices captured are emitted. for a standalone notice to be captured, it needs to be sent via the Broker. Let’s consider a ficticious standalone notice named “Foo”. It can be created and sent with this templated method:

auto broker = unf::Broker::Create(stage);
broker->Send<Foo>()

It can also be created separately and sent as follows:

auto broker = unf::Broker::Create(stage);
auto notice = Foo::Create();

broker->Send(notice);

Warning

If the notice is sent as follows, it will not be captured by the broker:

auto notice = Foo::Create();
notice->Send();

Standalone notices cannot be sent in Python.

Note

The sending process is usually handled by a Dispatcher.

A notice can be defined as “mergeable” or “unmergeable”. If a notice is defined as unmergeable, no consolidation will take place during a transaction. In the following example, one consolidated “Foo” notice will be sent at the end of the transaction if the notice was mergeable. Otherwise, the three notices are sent:

auto stage = PXR_NS::UsdStage::CreateInMemory();
auto broker = unf::Broker::Create(stage);

auto notice = Foo::Create();

// Indicate whether the notice can be merged.
printf(notice->IsMergeable())

{
    unf::NoticeTransaction transaction(broker);

    // The following notices will be captured by the broker during the
    // scope of the transaction.
    broker->Send(notice);
    broker->Send(notice);
    broker->Send(notice);
}

It is possible to start the transaction with a predicate function to indicate which notices are captured during the transaction. The following example will only filter in the “Foo” notices:

auto predicate = [&](const unf::UnfNotice::StageNotice& notice) {
    return (typeid(notice).name() == typeid(Foo).name());
};

{
    unf::NoticeTransaction transaction(broker, predicate);

    // ...
}

For convenience, a predicate has been provided to block all notices emitted during a transaction:

{
    unf::NoticeTransaction transaction(
        broker, unf::CapturePredicate::BlockAll());

    // ...
}

Default notices

By default, the broker will emit standalone equivalents for each USD notices:

Usd notices

Standalone Notices

UsdNotice::ObjectsChanged

UnfNotice::ObjectsChanged

UsdNotice::LayerMutingChanged

UnfNotice::LayerMutingChanged

UsdNotice::StageContentsChanged

UnfNotice::StageContentsChanged

UsdNotice::StageEditTargetChanged

UnfNotice::StageEditTargetChanged

Python bindings are also provided for each notice:

All of these notices are defined as mergeable and therefore will be consolidated per notice type during a transaction.

Note

These notices are handled by the StageDispatcher.

Custom notices

The UnfNotice::StageNotice interface can be safely derived as follows to create new notices:

class Foo : public unf::UnfNotice::StageNoticeImpl<Foo> {
public:
    Foo() = default;
    virtual ~Foo() = default;
};

By default, this notice will be mergeable, it can be made unmergeable as follows:

class Foo : public unf::UnfNotice::StageNoticeImpl<Foo> {
public:
    Foo() = default;
    virtual ~Foo() = default;

    bool IsMergeable() const override { return false; }
};

If the notice is mergeable and contain some data, the “Merge” method needs to be implemented to indicate how notices are consolidated. The “PostProcess” method could also be implemented to process the data after it has been merged with other notices:

using DataMap = std::unordered_map<std::string, std::string>;

class Foo : public unf::UnfNotice::StageNoticeImpl<Foo> {
public:
    Foo() = default;
    virtual ~Foo() = default;

    void Merge(Foo&& notice) override
    {
        for (const auto& it : notice._data) {
            _data[it.first] = std::move(it.second);
        }
    }

    void PostProcess() override
    {
        // ...
    }

private:
    DataMap _data;
};

Note

The copy constructor and assignment operator should be implemented as well if the notice contains data.

Warning

Custom standalone notices cannot be implemented in Python.